from sklearn.datasets import load_iris
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split,StratifiedKFold,KFold,GridSearchCV
import numpy as np
import joblib
# 曼哈顿距离 每一个轴上的距离和
# 欧式距离 两点之间线段最短
# K-近邻算法(K-Nearest Neighbors,简称KNN),根据K个邻居样本的类别来判断当前样本的类别; 离散型数据集才用
# 如果一个样本在特征空间中的k个最相似(最邻近)样本中的大多数属于某个类别,则该类本也属于这个类别
# 比如: 有10000个样本,选出7个到样本A的距离最近的,然后这7个样本中假设:类别1有2个,类别2有3个,类别3有2个.那么就认为A样本属于类别2,因为它的7个邻居中 类别2最多(近朱者赤近墨者黑)
# 就是在已经有的样本里面 并且这些样本都有结果了 找到 几个跟你距离最近的样本 你的结果大概就是这几个样本的结果里面的最多的那个
# 对于大规模数据集,计算量大,因为需要计算测试样本与所有训练样本的距离。 一万个数据每个都要算距离
# 对于高维数据,距离度量可能变得不那么有意义,这就是所谓的“维度灾难” 有些数据本来就没有意义
# 需要选择合适的k值和距离度量,这可能需要一些实验和调整 k的值
# API
# class sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, algorithm='auto')
# 参数:
# (1)n_neighbors:
# int, default=5, 默认情况下用于kneighbors查询的近邻数,就是K
# (2)algorithm:
# {‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’}, default=’auto’。找到近邻的方式,用那种数据结构保存 注意不是计算距离的方式,与机器学习算法没有什么关系,开发中请使用默认值'auto'
# 方法:
# (1) fit(x, y)
# 使用X作为训练数据和y作为目标数据
# (2) predict(X) 预测提供的数据,得到预测数据
def knn1():
iris1=load_iris()
scaler1=StandardScaler()
# print(iris1.keys())
# 数据标准化 要先把训练集标准化了 然后用训练集的数据取标准化测试集
x_train,x_test,y_train,y_test=train_test_split(iris1.data,iris1.target,train_size=0.4,test_size=0.3,random_state=666)
x_train_stand=scaler1.fit_transform(x_train)
x_test_stand=scaler1.transform(x_test)
# 创建KNN工具
mode1=KNeighborsClassifier(n_neighbors=5) # n_neighbors为K的值
# 使用工具进行fit 传入的是训练集 x 的训练集 和y的训练集 这样模型就得到了 接下来就是用模型预测结果
mode1.fit(x_train_stand,y_train)
# 传入测试集 进行预测
# y_predict=mode1.predict(x_test_stand)
# print(y_predict)
# print(y_test)
# 评估模型 正确数/总数
# 方法1 自己设计
# res=y_predict==y_test
# np.sum(res)/len(res)
# 方法2 使用集成的 返回正确率 传入的是xy的测试集
score1=mode1.score(x_test_stand,y_test)
print(score1)
# 使用模型进行对新的数据的推理 使用predict 新的数据标准化要用上面已经标准化过训练集的标准化工具
flow1=[[5,3,2,4],[3,6,2,1],[6,2,4,2]]
target_flow1=mode1.predict(scaler1.transform(flow1))
print(iris1.target_names[target_flow1])
# 保存模型后面可以使用 后缀一般为pkl 后面使用直接用model=joblib.load(文件路径)加载模型就行了 可以直接用来预测
"""
import joblib
# 保存模型
joblib.dump(estimator, "my_ridge.pkl")
# 加载模型
estimator = joblib.load("my_ridge.pkl")
#使用模型预测
y_test=estimator.predict([[0.4,0.2,0.4,0.7]])
print(y_test)
"""
# joblib.dump(mode1,"assets/model/knn_iris.pkl")
pass
# 模型选择与调优
# 交叉验证 test_train_split
# (1) 保留交叉验证HoldOut 就是分训练集和测试集的那个
# HoldOut Cross-validation(Train-Test Split)
# 在这种交叉验证技术中,**整个数据集被随机地划分为训练集和验证集**。根据经验法则,整个数据集的近70%被用作训练集,其余30%被用作验证集。也就是我们最常使用的,直接划分数据集的方法。
# 优点:很简单很容易执行。
# 缺点1:不适用于不平衡的数据集。假设我们有一个不平衡的数据集,有0类和1类。假设80%的数据属于 “0 “类,其余20%的数据属于 “1 “类。这种情况下,训练集的大小为80%,测试数据的大小为数据集的20%。
# 可能发生的情况是,所有80%的 “0 “类数据都在训练集中,而所有 “1 “类数据都在测试集中。因此,我们的模型将不能很好地概括我们的测试数据,因为它之前没有见过 “1 “类的数据。 现在用satisfy解决
# 缺点2:一大块数据被剥夺了训练模型的机会。
# 在小数据集的情况下,有一部分数据将被保留下来用于测试模型,这些数据可能具有重要的特征,而我们的模型可能会因为没有在这些数据上进行训练而错过。 好数据被划分到test了
# K-折交叉验证(K-fold) 数据集分几块 每个块 轮流当一次测试集
# (K-fold Cross Validation,记为K-CV或K-fold)
# K-Fold交叉验证技术中,整个数据集被划分为K个大小相同的部分。每个分区被称为 一个”Fold”。所以我们有K个部分,我们称之为K-Fold。一个Fold被用作验证集,其余的K-1个Fold被用作训练集。
# 该技术重复K次,直到每个Fold都被用作验证集,其余的作为训练集。
# 模型的最终准确度是通过取k个模型验证数据的平均准确度来计算的。
# (3) 分层k-折交叉验证Stratified k-fold 每一折的数据的结果 按照总的数据的结果比例等比例放入
# Stratified k-fold cross validation,
# K-折交叉验证的变种, 分层的意思是说在每一折中都保持着原始数据中各个类别的比例关系,
# 比如说:原始数据有3类,比例为1:2:1,采用3折分层交叉验证,那么划分的3折中,每一折中的数据类别保持着1:2:1的比例,这样的验证结果更加可信。
# API 用法一样 引用的类不一样
"""
from sklearn.model_selection import StratifiedKFold 分层k折
说明:普通K折交叉验证和分层K折交叉验证的使用是一样的 只是引入的类不同
from sklearn.model_selection import KFold k折
使用时只是KFold这个类名不一样其他代码完全一样
strat_k_fold=sklearn.model_selection.StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
n_splits划分为几个折叠
shuffle是否在拆分之前被打乱(随机化),False则按照顺序拆分
random_state随机因子
indexs=strat_k_fold.split(X,y)
返回一个可迭代对象,一共有5个折叠,每个折叠对应的是训练集和测试集的下标
然后可以用for循环取出每一个折叠对应的X和y下标来访问到对应的测试数据集和训练数据集 以及测试目标集和训练目标集
for train_index, test_index in indexs:
X[train_index] y[train_index] X[test_index ] y[test_index ]
"""
def stratified_KFold():
x,y=load_iris(return_X_y=True) # return_X_y直接返回data 和target了
# 创建StratifiedKFold工具
fold1=StratifiedKFold(n_splits=5,shuffle=True,random_state=666)
# 使用工具的split方法分割传入x,y 返回的是一个迭代器 for循环迭代器 每次的结果一个元组 里面是train数据的下标 和test数据的下标
iter1=fold1.split(x,y)
score_list=[]
for train_index,test_index in iter1:
# print(train_index,test_index,end="------")
model1=KNeighborsClassifier(n_neighbors=7)
# 传入train数据进行训练
model1.fit(x[train_index],y[train_index])
# 评估
score=model1.score(x[test_index],y[test_index])
score_list.append(score)
print(score_list)
print(np.mean(score_list))
pass
def kFold():
x,y=load_iris(return_X_y=True) # return_X_y直接返回data 和target了
# 创建StratifiedKFold工具
fold1=KFold(n_splits=5,shuffle=True,random_state=666)
# 使用工具的split方法分割传入x,y 返回的是一个迭代器 for循环迭代器 每次的结果一个元组 里面是train数据的下标 和test数据的下标
iter1=fold1.split(x,y)
score_list=[]
for train_index,test_index in iter1:
# print(train_index,test_index,end="------")
model1=KNeighborsClassifier(n_neighbors=7)
# 传入train数据进行训练
model1.fit(x[train_index],y[train_index])
# 评估
score=model1.score(x[test_index],y[test_index])
score_list.append(score)
print(score_list)
print(np.mean(score_list))
pass
# 超参数搜索 人为设置的那个K值
# 超参数搜索也叫网格搜索(Grid Search)
# 比如在KNN算法中,k是一个可以人为设置的参数,所以就是一个超参数。网格搜索能自动的帮助我们找到最好的超参数值。 自动获得最好的K值
"""
class sklearn.model_selection.GridSearchCV(estimator, param_grid)
说明:
同时进行交叉验证(CV)、和网格搜索(GridSearch),GridSearchCV实际上也是一个估计器(estimator),同时它有几个重要属性:
best_params_ 最佳参数
best_score_ 在训练集中的准确率
best_estimator_ 最佳估计器
cv_results_ 交叉验证过程描述
best_index_最佳k在列表中的下标
参数:
estimator: scikit-learn估计器实例
param_grid:以参数名称(str)作为键,将参数设置列表尝试作为值的字典
示例: {"n_neighbors": [1, 3, 5, 7, 9, 11]}
cv: 确定交叉验证切分策略,值为:
(1)None 默认5折
(2)integer 设置多少折
如果估计器是分类器,使用"分层k-折交叉验证(StratifiedKFold)"。在所有其他情况下,使用KFold。
"""
def grid_searchCV():
# 创建knn
estimator=KNeighborsClassifier()
# 创建 GridSearchCV工具 传入模型 和K值列表 注意K值列表传入方式 这个工具会根据数据的种类 分类 回归? 选择的模型划分方式 分类分层KFold 回归KFold
search1=GridSearchCV(estimator,param_grid={"n_neighbors":[5,7,9,10]})
x,y=load_iris(return_X_y=True)
search1.fit(x,y)
print(search1.best_score_)
print(search1.best_params_)
pass
if __name__=="__main__":
# knn1()
# stratified_KFold()
# kFold()
grid_searchCV()
pass