k近邻算法,朴素贝叶斯算法,分类模型评估,模型调优
k近邻算法
k近邻算法概述
- k近邻算法的定义
如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。 - 距离计算公式
两个样本的距离可以通过如下公式计算,又叫欧式距离
若点a,b坐标如下:a(a1,a2,a3),b(b1,b2,b3)
则点a,b间的距离计算公式如下:
k近邻算法代码实现
- API:sklearn.neighbors.KNeighborsClassifier(n_neighbors=5,algorithm=‘auto’)
- n_neighbors:int,可选(默认= 5),k_neighbors查询默认使用的邻居数
- algorithm:{‘auto’,‘ball_tree’,‘kd_tree’,‘brute’},可选用于计算最近邻居的算法:‘ball_tree’将会使用 BallTree,‘kd_tree’将使用 KDTree。‘auto’将尝试根据传递给fit方法的值来决定最合适的算法。 (不同实现方式影响效率)
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
def knncls():
# 读取数据
data = pd.read_csv(r"E:\data\facebook-v-predicting-check-ins\train.csv\train.csv")
# 缩小数据集范围
data = data.query("x>1.0&x<1.25&y>1.0&y<1.25")
# print(data.head(10))
# 把时间戳特征转化为天、小时、周,删除时间戳特征
time_value = pd.to_datetime(data['time'],unit='s')
# print(type(time_value))
time_value = pd.DatetimeIndex(time_value)
data['day'] = time_value.day
data['hour'] = time_value.hour
data['weekday'] = time_value.weekday
data = data.drop(['time'],axis =1)
print(data.head(10))
# 减少目标值个数,把签到数量小于3的目标位置删除
place_count = data.groupby('place_id').count()
tf = place_count[place_count.row_id>3].reset_index()
data = data[data['place_id'].isin(tf.place_id)]
# 取出特征值和目标值
y = data['place_id']
x = data.drop(['place_id'],axis=1)
# 将数据分割为训练集和测试集
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.25)
# 标准化(特征工程)
std = StandardScaler()
x_train = std.fit_transform(x_train)
x_test = std.fit_transform(x_test)
# knn算法流程
knn = KNeighborsClassifier()
knn.fit(x_train,y_train)
y_predict = knn.predict(x_test)
print("预测目标签到位置为:",y_predict)
print(knn.score(x_test,y_test))
return None
if __name__ == '__main__':
knncls()
输出结果:
row_id x y accuracy place_id day hour weekday
2450 2450 1.2427 1.1598 224 6026000998 5 21 0
2636 2636 1.0256 1.0355 148 5152560199 9 19 4
2766 2766 1.0814 1.0391 70 8517315303 6 20 1
3164 3164 1.0442 1.0195 117 5152560199 6 18 1
3342 3342 1.0528 1.0103 9 1109153906 1 15 3
4755 4755 1.2161 1.0410 123 8330850914 10 1 5
6245 6245 1.0204 1.0027 20 2013736336 2 22 4
7675 7675 1.2380 1.0588 83 6852043069 8 23 3
8082 8082 1.0081 1.1633 165 5028377747 8 17 3
9565 9565 1.1783 1.0316 7 5887940395 3 23 5
预测目标签到位置为: [2229770936 2229770936 4338348850 ... 1650177003 8765197091 1985125281]
0.33544093178036605
k近邻算法的评价
- k取不同值时对算法性能的影响
k值取很小:容易受异常点影响
k值取很大:容易受最近数据太多导致比例变化 - k近邻算法优缺点
优点:简单,易于理解,易于实现,无需估计参数,无需训练
缺点:1)懒惰算法,对测试样本分类时的计算量大,内存开销大;2)必须指定K值,K值选择不当则分类精度不能保证 - 使用场景:小数据场景
朴素贝叶斯算法
朴素贝叶斯算法概述
以贝叶斯公式为概率基础的模型,前提条件为事件之间相互独立
朴素贝叶斯代码实现
- API: sklearn.naive_bayes.MultinomialNB(alpha = 1.0)
朴素贝叶斯分类
alpha:拉普拉斯平滑系数
import pandas as pd
from sklearn.datasets import fetch_20newsgroups
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
def naviebayes():
news = fetch_20newsgroups(subset= 'all')
# 数据分割
x_train,x_test,y_train,y_test = train_test_split(news.data,news.target,test_size=0.25)
# 特征抽取
tf = TfidfVectorizer()
# 转化为one-hot编码
x_train = tf.fit_transform(x_train)
x_test = tf.transform(x_test)
# 朴素贝叶斯算法过程
mlt = MultinomialNB(alpha=1.0)
mlt.fit(x_train,y_train)
y_predict = mlt.predict(x_test)
print("预测的文章类别为:", y_predict)
print("准确率为:", mlt.score(x_test,y_test))
return None
if __name__ == '__main__':
naviebayes()
输出结果:
预测的文章类别为: [11 14 6 ... 17 3 7]
准确率为: 0.8404074702886248
朴素贝叶斯的评价
优点:
- 朴素贝叶斯模型发源于古典数学理论,有稳定的分类效率。
- 对缺失数据不太敏感,算法也比较简单,常用于文本分类。
- 分类准确度高,速度快。
缺点:需要知道先验概率P(F1,F2,…|C),因此在某些时候会由于假设的先验模型(事件相互独立)的原因导致预测效果不佳。
分类模型的评估
分类模型评估概述
- estimator.score()
一般最常见使用的是准确率,即预测结果正确的百分比 - 其他评价指标:精确率(Precision),召回率(Recall),F1-score
- 精确率:预测结果为正例样本中真实为正例的比例(查得准)
- 召回率:真实为正例的样本中预测结果为正例的比例(查的全,对正样本的区分能力)
- F1-score:反映了模型的稳健性
分类模型评估代码实现
- API:sklearn.metrics.classification_report(y_true, y_pred, target_names=None)
y_true:真实目标值
y_pred:估计器预测目标值
target_names:目标类别名称
return:每个类别精确率与召回率 - 以文本分类为例的模型评估
import pandas as pd
from sklearn.datasets import fetch_20newsgroups
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import classification_report
def naviebayes():
news = fetch_20newsgroups(subset= 'all')
# 数据分割
x_train,x_test,y_train,y_test = train_test_split(news.data,news.target,test_size=0.25)
# 特征抽取
tf = TfidfVectorizer()
# 转化为one-hot编码
x_train = tf.fit_transform(x_train)
x_test = tf.transform(x_test)
# 朴素贝叶斯算法过程
mlt = MultinomialNB(alpha=1.0)
mlt.fit(x_train,y_train)
y_predict = mlt.predict(x_test)
print("预测的文章类别为:", y_predict)
print("准确率为:", mlt.score(x_test,y_test))
print("每个类别的精确率和召回率:",classification_report(y_test,y_predict,target_names=news.target_names))
return None
if __name__ == '__main__':
naviebayes()
输出结果:
预测的文章类别为: [15 15 10 ... 17 4 7]
准确率为: 0.849320882852292
每个类别的精确率和召回率: precision recall f1-score support
alt.atheism 0.95 0.65 0.77 223
comp.graphics 0.87 0.79 0.83 225
comp.os.ms-windows.misc 0.89 0.85 0.87 245
comp.sys.ibm.pc.hardware 0.75 0.85 0.80 246
comp.sys.mac.hardware 0.90 0.86 0.88 234
comp.windows.x 0.96 0.85 0.90 254
misc.forsale 0.92 0.75 0.83 246
rec.autos 0.89 0.94 0.92 224
rec.motorcycles 0.94 0.95 0.95 252
rec.sport.baseball 0.96 0.96 0.96 267
rec.sport.hockey 0.92 0.98 0.95 243
sci.crypt 0.73 0.98 0.83 238
sci.electronics 0.91 0.77 0.83 251
sci.med 0.98 0.84 0.91 279
sci.space 0.90 0.95 0.92 252
soc.religion.christian 0.46 0.98 0.63 221
talk.politics.guns 0.84 0.95 0.89 247
talk.politics.mideast 0.91 0.97 0.94 236
talk.politics.misc 0.99 0.66 0.79 173
talk.religion.misc 0.96 0.15 0.27 156
micro avg 0.85 0.85 0.85 4712
macro avg 0.88 0.83 0.83 4712
weighted avg 0.88 0.85 0.85 4712
模型的调优
相关概念
交叉验证
- 目的:为了让被评估的模型更加准确可信
- 交叉验证过程:将拿到的数据,分为训练和验证集。以下图为例:将数据分成5份,其中一份作为验证集。然后经过5次(组)的测试,每次都更换不同的验证集。即得到5组模型的结果,取平均值作为最终结果。又称5折交叉验证。
网格搜索
- 目的:对超参数进行搜索
- 使用网格搜索的原因:通常情况下,有很多参数是需要手动指定的(如k-近邻算法中的K值),这种叫超参数。但是手动过程繁杂,所以需要对模型预设几种超参数组合。每组超参数都采用交叉验证来进行评估。最后选出最优参数组合建立模型。
模型调优代码实现
- API:sklearn.model_selection.GridSearchCV(estimator, param_grid=None,cv=None)
对估计器的指定参数值进行详尽搜索
estimator:估计器对象
param_grid:估计器参数(dict){“n_neighbors”:[1,3,5]}
cv:指定几折交叉验证
fit:输入训练数据
score:准确率
结果分析:
best_score_:在交叉验证中测试的最好结果
best_estimator_:最好的参数模型
cv_results_:每次交叉验证后的测试集准确率结果和训练集准确率结果 - 以k近邻算法为例的模型调优
import pandas as pd
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
def knncls():
# 读取数据
data = pd.read_csv(r"E:\data\facebook-v-predicting-check-ins\train.csv\train.csv")
# 缩小数据集范围
data = data.query("x>1.0&x<1.25&y>1.0&y<1.25")
# print(data.head(10))
# 把时间戳特征转化为天、小时、周,删除时间戳特征
time_value = pd.to_datetime(data['time'],unit='s')
# print(type(time_value))
time_value = pd.DatetimeIndex(time_value)
data['day'] = time_value.day
data['hour'] = time_value.hour
data['weekday'] = time_value.weekday
data = data.drop(['time'],axis =1)
print(data.head(10))
# 减少目标值个数,把签到数量小于3的目标位置删除
place_count = data.groupby('place_id').count()
tf = place_count[place_count.row_id>3].reset_index()
data = data[data['place_id'].isin(tf.place_id)]
# 取出特征值和目标值
y = data['place_id']
x = data.drop(['place_id'],axis=1)
# 将数据分割为训练集和测试集
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.25)
# 标准化(特征工程)
std = StandardScaler()
x_train = std.fit_transform(x_train)
x_test = std.fit_transform(x_test)
# knn算法流程
knn = KNeighborsClassifier()
# knn.fit(x_train,y_train)
# y_predict = knn.predict(x_test)
# print("预测目标签到位置为:",y_predict)
# print(knn.score(x_test,y_test))
# 对knn的超参数进行优化
# 参数备选范围
param = {"n_neighbors": [3,5,7]}
# 网格搜索
gc = GridSearchCV(knn, param_grid=param, cv = 5)
gc.fit(x_train,y_train)
# 预测准确率
print("在测试集上准确率:",gc.score(x_test,y_test))
print("在交叉验证中的最好结果:",gc.best_score_)
print("最好的模型是:",gc.best_estimator_)
print("每个超参数每次交叉验证的结果为:",gc.cv_results_)
return None
if __name__ == '__main__':
knncls()
输出结果:
在测试集上准确率: 0.337936772046589
在交叉验证中的最好结果: 0.3361069506850835
最好的模型是: KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=None, n_neighbors=7, p=2,
weights='uniform')
每个超参数每次交叉验证的结果为: {'mean_fit_time': array([0.10036383, 0.01190853, 0.01141105]), 'std_fit_time': array([0.16776234, 0.00257666, 0.00149117]), 'mean_score_time': array([0.25650139, 0.19496632, 0.20525126]), 'std_score_time': array([0.15538092, 0.01424587, 0.00708061]), 'param_n_neighbors': masked_array(data=[3, 5, 7],
mask=[False, False, False],
fill_value='?',
dtype=object), 'params': [{'n_neighbors': 3}, {'n_neighbors': 5}, {'n_neighbors': 7}], 'split0_test_score': array([0.28536913, 0.32456376, 0.33583893]), 'split1_test_score': array([0.28361858, 0.31893507, 0.33441999]), 'split2_test_score': array([0.3024093 , 0.3303794 , 0.34561063]), 'split3_test_score': array([0.29421721, 0.32327221, 0.32665726]), 'split4_test_score': array([0.2969697 , 0.32871573, 0.33795094]), 'mean_test_score': array([0.29239474, 0.32512343, 0.33610695]), 'std_test_score': array([0.00711984, 0.00407093, 0.00607468]), 'rank_test_score': array([3, 2, 1]), 'split0_train_score': array([0.58033841, 0.52810796, 0.49531534]), 'split1_train_score': array([0.57709466, 0.52697616, 0.4928203 ]), 'split2_train_score': array([0.57810766, 0.5225444 , 0.48994173]), 'split3_train_score': array([0.57982323, 0.5222345 , 0.49240436]), 'split4_train_score': array([0.57807993, 0.52513391, 0.49031726]), 'mean_train_score': array([0.57868878, 0.52499938, 0.4921598 ]), 'std_train_score': array([0.00120481, 0.00233497, 0.00193744])}