Kaggle比赛San Francisco Crime Classification分析

题目参见San Francisco Crime Classification

这里简要介绍下这个题目的注意事项:

  1. 多分类。为每个类输出相应类的概率
  2. 评估的时候使用的是multi-class log loss

特征工程

无疑,当前使用的是最原始的特征。我们并未对特征进行一定的调整,而是粗暴的直接拿来使用。

目前特征分成以下几类:

  • 时间类,包括月份、天、时、具体星期几
  • 地址类,包括经纬度以及所在辖区

Baseline Model

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from sklearn.model_selection import cross_val_score
from sklearn import linear_model
from sklearn import metrics
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV



targets = ['ARSON', 'ASSAULT', 'BAD CHECKS', 'BRIBERY', 'BURGLARY',
     'DISORDERLY CONDUCT', 'DRIVING UNDER THE INFLUENCE', 'DRUG/NARCOTIC',
     'DRUNKENNESS', 'EMBEZZLEMENT', 'EXTORTION', 'FAMILY OFFENSES',
     'FORGERY/COUNTERFEITING', 'FRAUD', 'GAMBLING', 'KIDNAPPING',
     'LARCENY/THEFT', 'LIQUOR LAWS', 'LOITERING', 'MISSING PERSON',
     'NON-CRIMINAL', 'OTHER OFFENSES', 'PORNOGRAPHY/OBSCENE MAT',
     'PROSTITUTION', 'RECOVERED VEHICLE', 'ROBBERY', 'RUNAWAY',
     'SECONDARY CODES', 'SEX OFFENSES FORCIBLE', 'SEX OFFENSES NON FORCIBLE',
     'STOLEN PROPERTY', 'SUICIDE', 'SUSPICIOUS OCC', 'TREA', 'TRESPASS',
     'VANDALISM', 'VEHICLE THEFT', 'WARRANTS', 'WEAPON LAWS']

def feature_engineer(data):
  data["Month"] = data["Dates"].map(lambda x: int(x[5:7]))
  data["Day"] = data["Dates"].map(lambda x:int(x[8:10]))
  data["Hour"] = data["Dates"].map(lambda x:int(x[11:13]))
  data["District_No"] = data["PdDistrict"].map(lambda x:districts[x])
  data["Weekday"] = data["DayOfWeek"].map(lambda x:weekdays[x])
  return data



train = pd.read_csv("/Users/maodou/Desktop/crimeclass/train.csv")
test = pd.read_csv("/Users/maodou/Desktop/crimeclass/test.csv")
districts = {c:i for i,c in enumerate(train['PdDistrict'].unique())}
weekdays = {c:i for i,c in enumerate(train['DayOfWeek'].unique())}

train = feature_engineer(train)
train["targets"] = train["Category"].map(lambda x:targets.index(x))
test = feature_engineer(test)

X_all = ["Month", "Day", "Hour", "Weekday", "District_No", "X", "Y"]

forest = RandomForestClassifier(n_estimators=100).fit(train[X_all],train['targets'])
result = forest.predict_proba(test[X_all])

submission = pd.DataFrame({targets[p] : [result[i][p] for i in range(len(result))] for p in range(len(result[0]))})

submission['Id'] = [i for i in range(len(submission))]

submission.to_csv("submission_idx_baseline_tuning.csv.gz", index=False, compression='gzip')

这个模型结果3.8分。不够好。下面将优化。

Baseline代码中的几个Tricks

  1. 从最终predict出来的矩阵转到submission中的multi-class的DataFrame
submission = pd.DataFrame({targets[p] : [result[i][p] for i in range(len(result))] for p in range(len(result[0]))})

一句话初始化转换。
如果使用多重for循环,先默认初始化再复制的形式,会导致运行时间大大增长

  1. 单独定义feature_engineer函数,避免混乱的train和test同时变换
  2. 通过选择train中的指定列来定义训练数据,避免重新构造新的data frame。直接train[X_selected]即可只选择X_selected中选定的列数组

调参

RandomForest调参

RandomForest大部分参数使用默认的参数即可。涉及到调参的主要是以下几个:

  • n_estimators : 森林中树的个数, 一般来说树的个数越多越好。故该参数应该设置成能设置的最大值,不需要重新搜索
  • max_depth : 树的深度 一般设置为10到100之间
  • min_samples_split : 向下继续分支的最小样本数
  • max_features : 每颗树使用的特征数(log2, sqrt(auto), 等)

使用GridSearchCV来搜索最佳值

params = {"max_features":("log2", "sqrt")}

gsearch = GridSearchCV(estimator=RandomForestClassifier(n_estimators=100, max_depth=10, min_samples_split=100), param_grid = params, scoring = "neg_log_loss")
gsearch.fit(train[X_all], train["targets"])

print(gsearch.grid_scores_)

print("best params")

print(gsearch.best_params_)

print("best scores")

print(gsearch.best_score_)

这样逐个确认最佳参数。局部最优值不代表全局,可目前只能用这个方法啦。

最终使用参数如下:

  forest = RandomForestClassifier(n_estimators=100, max_depth=10, min_samples_split=100, max_features="log2", oob_score=True).fit(train[X_all],train['targets'])

得分2.4 , 大大提高了排名。

对比

优化点 Score
RF默认 3.84746
不使用街角特征 2.43471
不变换经度纬度&不使用街角特征 2.4061
不变换经度纬度&不使用街角特征&不变换Hour 2.40072

使用KNN进行预测

主体代码一样,关键代码如下:

knn = KNeighborsClassifier(n_neighbors = 4, weights = 'uniform').fit(train[X_all], train["targets"])
result = knn.predict_proba(test[X_all])

最终效果,得分21.48253。

简直不可接受啊!!!!

现在进行调参。

这个n_neighbors参数很多文章说使用3到5就行了。但经试验,发现这个值会对模型结果影响特别大。

params = {"n_neighbors": range(100 , 801, 100)}

gsearch = GridSearchCV(estimator=KNeighborsClassifier( weights = 'uniform'), param_grid = params , scoring = "neg_log_loss")

gsearch.fit(train[X_all], train["targets"])

print(gsearch.grid_scores_)

print("best params")

print(gsearch.best_params_)

print("best scores")

print(gsearch.best_score_)

在该实验中,发现n_neighbors取到800能达到更好的效果。public score能到2.77分。目前该值还是越大越好。因为本人电脑性能问题,暂不对n_neighbors的最优值进行探索

通过对KNN和RF的对比可以发现,使用不同的算法,很可能会大大影响到找到使模型快速收敛到最佳状态的参数

你可能感兴趣的:(Kaggle比赛San Francisco Crime Classification分析)