kaggle-旧金山犯罪分类详细讲解 (朴素贝叶斯、逻辑回归、随机森林方法)

前言

记得一位老先生说,如果你所讲的知识不能让一个8岁孩子听懂,说明你还是没真正掌握。
本文秉持这样的理念,先给出全部代码,让读者一窥全貌,再逐段详细讲解。

旧金山犯罪分类kaggle地址

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

  • 23
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值