前言
记得一位老先生说,如果你所讲的知识不能让一个8岁孩子听懂,说明你还是没真正掌握。
本文秉持这样的理念,先给出全部代码,让读者一窥全貌,再逐段详细讲解。
0、旧金山犯罪分类代码
import pandas as pd
import numpy as np
# 1、载入数据
train = pd.read_csv('dataset/train.csv', parse_dates = ['Dates'])
test = pd.read_csv('dataset/test.csv', parse_dates = ['Dates'])
# 2、数据预处理,对category进行编码
from sklearn import preprocessing
label = preprocessing.LabelEncoder()
crime = label.fit_transform(train.Category) #进行编号
# 3、对Dates、DayOfWeek、PdDistrict三个特征进行二值化处理,因为3个在训练集和测试集都出现
days = pd.get_dummies(train.DayOfWeek)
district = pd.get_dummies(train.PdDistrict)
hour = pd.get_dummies(train.Dates.dt.hour)
train_data = pd.concat([days, district, hour], axis=1) # 将days district hour连成一张表 ,当axis = 1的时候,concat就是行对齐,然后将不同列名称的两张表合并
train_data['crime'] = crime # 在DataFrame数据结构 表的 最后加一列,在本例中相当于标签
# 实际上,只使用了三个特征,和犯罪类型作为标签 即只使用了原始数据集中的4列数据
# 但是train_data这张表 其实是将3个特征展开成了几十个特征 对应一个标签
# 针对测试集做同样的处理
days = pd.get_dummies(test.DayOfWeek)
district = pd.get_dummies(test.PdDistrict)
hour = pd.get_dummies(test.Dates.dt.hour)
test_data = pd.concat([days, district, hour], axis=1)
# 4、将样本几何分割成训练集和验证集(70%训练,30%验证),返回的是划分好的训练集 和 验证集
from sklearn.cross_validation import train_test_split
training, validation = train_test_split(train_data, train_size=0.7)
# 5、朴素贝叶斯
from sklearn.metrics import log_loss
from sklearn.naive_bayes import BernoulliNB
model = BernoulliNB()
feature_list = training.columns.tolist() #将列名字转换为列表
feature_list = feature_list[:len(feature_list) - 1] # 选取的特征列 最后一列是标签,不能要,注意列表是左闭右开
model.fit(training[feature_list], training['crime']) #根据给定的训练数据拟合模型
predicted = np.array(model.predict_proba(validation[feature_list])) #validation[feature_list] 不包括最后一列crime 的验证集 model.predict_proba 第 i 行 第 j 列上的数值是模型预测第 i 个预测样本 为某个【标签】的概(表头是标签类别),从小到大排序的 predicted是在验证集上的结果
print ("朴素贝叶斯log损失为 %f" % (log_loss(validation['crime'], predicted))) #多分类的对数损失
# 6、其他模型等 (逻辑回归,随机森林)
from sklearn.linear_model import LogisticRegression
model_LR = LogisticRegression(C=0.1)
model_LR.fit(training[feature_list], training['crime'])
predicted = np.array(model_LR.predict_proba(validation[feature_list]))
print ("逻辑回归log损失为 %f" %(log_loss(validation['crime'], predicted)))
from sklearn.ensemble import RandomForestClassifier
model_RF = RandomForestClassifier()
model_RF.fit(training[feature_list], training['crime'])
predicted = np.array(model_RF.predict_proba(validation[feature_list]))
print ("随机森林log损失为 %f" %(log_loss(validation['crime'], predicted)))
# 7、在测试集上运行
test_predicted = np.array(model.predict_proba(test_data[feature_list])) # model为朴素贝叶斯
# 8、保存结果
col_names = np.sort(train['Category'].unique()) # 唯一,按首字母从小到大排序
result = pd.DataFrame(data=test_predicted, columns=col_names) # 合成DataFrame数据结构的表 col_names是排序的,test_predicted由于predict_proba,所以也是按顺序的
result['Id'] = test['Id'].astype(int) # 从 dtype: int64 变为 dtype: int32 并且在最后加一列result['Id']
result.to_csv('test_output.csv', index=False) #保存
print ("finish")
一、载入数据
# 1、载入数据
train = pd.read_csv('dataset/train.csv', parse_dates = ['Dates'])
test = pd.read_csv('dataset/test.csv', parse_dates = ['Dates'])
利用pandas库中的 read_csv()函数读取本地csv文件,包括训练集和测试集。
返回类型为 DataFrame 数据结构的 表。(DataFrame解释见第九部分)
二、数据预处理(选定标签)
# 2、数据预处理,对category进行编码
from sklearn import preprocessing
label = preprocessing.LabelEncoder()
crime = label.fit_transform(train.Category) #对train中category列进行编号,作为标签。
使用sklearn库的preprocessing.labelEncoder()
其作用是将标签数值化为0–n-1,其中n为标签的个数(不算重复的)
示例:
可以看到
(1)相同标签编号为相同数字
(2)其是根据首字母顺序编码标签,首字母为a的编号为0。即其是按照一定顺序编码。
因此,对train中category列进行编号,作为标签。
对标签进行编号,代替非数值的标签
三、特征选择和二值化处理
3.1 特征选择
首先要选取特征,通过对训练集和测试集的分析,
训练集有8个特征 分别为
Dates
Category
Descrip
DayOfWeek
PdDistrict
Resolution
Address
X
Y
测试集有6个特征(不算id列) 分别为
Id
Dates
DayOfWeek
PdDistrict
Address
X
Y
我们可以看到,同时出现在训练集和测试集的特征有6个,即可选训练特征为
Dates
DayOfWeek
PdDistrict
Address
X
Y
通过观察可以看到Address 与一组x,y是对应的,因此,选取address,舍弃x,y特征,所以,可选特征变为4个
Dates
DayOfWeek
PdDistrict
Address
再对四个特征进行分析,
address 特征的unique为23228 (unique为特征address中去除重复后的数量)
dayofweek 特征的unique为7
pddistrict 特征的unique为10
dates特征的unique为389257,但是日期特征我们只取小时数,即 0-23可以理解unique为24
因此,由于address的unique为23228,过高,在进行矩阵运算时,会出现维数爆炸,因此,无法选取才特征。unique和维数的关系下文会说。
因此,最终选取特征为,
Dates
DayOfWeek
PdDistrict
即最终维数为7+10+24=41维度,第42维为标签crime列。
3.2 特征二值化处理
对上述三个特征做二值化处理,
days = pd.get_dummies(train.DayOfWeek)
district = pd.get_dummies(train.PdDistrict)
hour = pd.get_dummies(train.Dates.dt.hour)
get_dummies() 可以直接拿到一个二值化的01向量 (将分类变量转换为虚拟/指示符变量)
例如
第0个出现a,第1个出现b,第2个出现c,第3个出现a ,列表abca转换成了如上二值表,
该二值表的行数为列表的长度,列数为列表中的元素种类个数(唯一)。
列入dayofweek这个特征,有7个元素种类,即周一-周日,所以列为7列
行数为列表长度,即
地区和hour是一样的,其中hour为train.Dates.dt.hour,其中.dt.hour为日期中的小时数。如下:
train_data = pd.concat([days, district, hour], axis=1)
pd.concat将days district hour三个特征连成一张表 ,当axis = 1的时候,concat就是行对齐,然后将不同列名称的两张表合并,效果如下:
train_data['crime'] = crime
在DataFrame数据结构 表的 最后加一列,在本例中相当于标签
综上所述,实际上,只使用了三个特征,和犯罪类型作为标签 即只使用了原始数据集中的4列数据。
但是train_data这张表 其实是将3个特征展开成了41个特征 对应一个标签,即前面的41维,加1列标签。
即 train_data总共42列。
也正因此,不能选取address特征,其展开后会有41+23228个“特征”。
(2)同理,对测试集中这个三个特征做相同操作,
# 针对测试集做同样的处理
days = pd.get_dummies(test.DayOfWeek)
district = pd.get_dummies(test.PdDistrict)
hour = pd.get_dummies(test.Dates.dt.hour)
test_data = pd.concat([days, district, hour], axis=1)
test_data是一张41列的dataframe数据结构的表。无标签列,这区别于训练集 train_data。
至此,原始数据已被处理完毕,形成了真正可用的训练集和测试集。
四、划分训练集和验证集
第三部分所说的训练集,其实就是整个样本空间,即所有样本集合(样本集),在本章,我们将样本集,划分为训练集和验证集两个部分(这就是 这里的训练集和第三部分的训练集不是一个东西的区别)
(注:区别验证集和真正测试集的概念)
from sklearn.cross_validation import train_test_split
training, validation = train_test_split(train_data, train_size=0.7)
将样本几何分割成训练集和验证集(70%训练,30%验证),返回的是划分好的训练集 和 验证集,为dataframe数据结构(表)。
train_test_split
函数用于将矩阵随机划分为训练子集和测试子集,并返回划分好的训练集测试集样本和训练集测试集标签。 (这里其实是 将样本划分为 训练集 和 验证集,并非是用于测试的测试集,那是单独另一个)
注意,前面吧标签加到train_data里了,这么做比较方便,如果不这么做,例如如下
输入数据集X,和标签y
返回四个值,
X_train为训练集数据,y_train为训练集标签
X_test为验证集数据,y_test为验证集标签
五、朴素贝叶斯方法
在使用之前,现获取训练集的所有列名字,注意,不要最后一列的标签列的名字
feature_list = training.columns.tolist() #将列名字转换为列表
# 除去最后一列标签列,选取的特征列 注意列表是左闭右开
feature_list = feature_list[:len(feature_list) - 1]
下面使用贝叶斯处理
from sklearn.naive_bayes import BernoulliNB
model = BernoulliNB()
model.fit(training[feature_list], training['crime']) #根据给定的训练数据拟合模型
fit(X, y, sample_weight=None)
根据给定的训练数据拟合模型。X训练集 y为标签,第三个参数不用
例:model.fit(X,y) 其中model可以为如下内容等等,例如:
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(C=0.1) #逻辑回归
model.fit(training[feature_list], training['crime'])
from sklearn.svm import SVC
model = SVC() #SVM
model.fit(training[feature_list], training['crime'])
from sklearn.ensemble import AdaBoostClassifier
model = AdaBoostClassifier(n_estimators=100) #迭代100次 adaboost
model.fit(training[feature_list], training['crime'])
from sklearn.tree import DecisionTreeClassifier
# fit a CART model to the data
model = DecisionTreeClassifier() #决策树
model.fit(training[feature_list], training['crime'])
from sklearn.naive_bayes import GaussianNB
model = GaussianNB() #gauss贝叶斯
model.fit(training[feature_list], training['crime'])
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier() #随机森林
model.fit(training[feature_list], training['crime'])
predicted = np.array(model.predict_proba(validation[feature_list]))
#validation[feature_list] 不包括最后一列crime 的验证集
model.predict_proba 第 i 行 第 j 列上的数值是模型预测第 i 个预测样本 为某个【标签】的概率(表头是标签类别),从小到大排序的 ,(并且每一行的概率和为1。) ,predicted是在验证集上的结果。显示如下:
sklearn中predict_proba用法注意和predict的区别,具体参见
https://blog.csdn.net/m0_37870649/article/details/79549142
对验证集结果进行评价:kaggle上面一般使用log_loss值检测测试集的好坏,从而进行用户排名。
from sklearn.metrics import log_loss
print ("朴素贝叶斯log损失为 %f" % (log_loss(validation['crime'], predicted))) #多分类的对数损失
log_loss函数的数学公式和原理可以上网查查,在这里就不介绍了。结果如下:
至此,给出了朴素贝叶斯的完整训练过程,其他分类方法类似,大家可以尝试下,具体看下一部分。
六、其他模型(逻辑回归,随机森林)
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(C=0.1)
model.fit(training[feature_list], training['crime'])
predicted = np.array(model.predict_proba(validation[feature_list]))
print ("逻辑回归log损失为 %f" %(log_loss(validation['crime'], predicted)))
结果为:
随机森林方法
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier()
model.fit(training[feature_list], training['crime'])
predicted = np.array(model.predict_proba(validation[feature_list]))
print ("随机森林log损失为 %f" %(log_loss(validation['crime'], predicted)))
结果为:
因此,朴素贝叶斯方法效果最好,我们选取朴素贝叶斯的模型。
七、在测试集上运行
test_predicted = np.array(model.predict_proba(test_data[feature_list]))
具体解释参见前面。
八、保存结果
col_names = np.sort(train['Category'].unique()) # 唯一,按首字母从小到大排序
train['Category'].unique()
为Category中‘唯一’的名称(也就是去掉重复的),并对其进行排序(从小到大)。
排序是因为前面的test_predicted返回的概率,也是按顺序返回的。
(col_names是排序的,test_predicted由于predict_proba,所以也是按顺序的)
result = pd.DataFrame(data=test_predicted, columns=col_names)
# 合成DataFrame数据结构的表
即给test_predicted加上列名称。这样可以看出每个概率对应的category。返回为result表
result['Id'] = test['Id'].astype(int)
# 从 dtype: int64 变为 dtype: int32 并且在最后加一列result['Id']
这条语句就是在result表最后加一列id编号,和前面加上crime标签列是同一个意思。
result.to_csv('test_output.csv', index=False) #保存
将结果保存到test_output.csv文件(文件不存在会自动创建)。
至此,完成了整个项目代码的编写,下面可以将test_output.csv上传到kaggle上进行评比。
九、一些解释
(1)
特征选择:选取几个特征
特征提取:映射,比如映射到低维
注意区分这两个概念
(2)
one-hot编码
所有的分类型字段最初都是整数形式的。依据机器学习算法,序数值型的编号会让模型认为一个类型比另一个类型有更大的关联。例如,阿根廷的编号是1,巴西的是2,算法会推测巴西的代表性是阿根廷的两倍。为了处理这个问题,通常会使用诸如单热编码(One-Hot Encoding, OHE)的技巧,这种方法会把每个分类转换成一个稀疏的向量。在这个稀疏的向量中,除了编号值对应的位置,其他位置都是0。
https://blog.csdn.net/google19890102/article/details/44039761
(3)
pandas.DataFrame
二维大小可变,具有标记轴(行和列)的潜在异构表格数据结构。算术运算在行和列标签上对齐。可以认为是Series对象的一个类似字典的容器。
(4)
sklearn中logloss函数的输入问题
https://blog.csdn.net/ybdesire/article/details/73695163