简介 Otto Group Product Classification Challenge 是Kaggle上目前正在进行的一个比赛,目前已1000+队伍参赛,由Otto公司赞助1W美刀,数据也是来自于该公司的产品,提供了train.csv、test.csv、samplesubmission.csv三份数据。train.csv里包含了6万多个样本,每个样本有一个id,93个特征值feat_1~feat_93,以及类别target,一共9种类别:class_1~class_9。test.csv里有14万多测试样本,只有id以及93个特征,参赛者的任务是对这些样本分类。
提交结果的格式 官网上给出的格式:
1 2 3 id,Class_1,Class_2,Class_3,Class_4,Class_5,Class_6,Class_7,Class_8,Class_9 1 ,0.0 ,0.0 ,0.0 ,0.0 ,1.0 ,0.0 ,0.0 ,0.0 ,0.0 2 ,0.0 ,0.2 ,0.3 ,0.3 ,0.0 ,0.0 ,0.1 ,0.1 ,0.0
也就是说,可以不用给出确切的类别,给出属于各个类别的概率也行。这一点很重要,我一开始没注意看,浪费了许多精力瞎折腾。所以但凡比赛,官网给出的信息最好都仔细看一遍。
评分标准 评分的公式见:这里
我稍微解释一下这条公式,i表示样本,j表示类别。Pij代表第i个样本属于类别j的概率,如果第i个样本真的属于类别j,则yij等于1,否则为0。
假如你将所有的测试样本都正确分类,所有pij都是1,那每个log(pij)都是0,最终的logloss也是0。
假如第1个样本本来是属于1类别的,但是你给它的类别概率pij=0.1,那logloss就会累加上log(0.1)这一项。我们知道这一项是负数,而且pij越小,负得越多,如果pij=0,将是无穷。这会导致这种情况:你分错了一个,logloss就是无穷。这当然不合理,为了避免这一情况,官方是这样处理的:
也就是说最小不会小于10^-15。
开始解题 废话终于说完,进入主题。下面讲一个很naive的solution,基本上不用动脑,只要会简单地使用sklearn、numpy,就能打败目前一半的参赛队伍。
代码放在我的github上wepe/Kaggle-Solution ,分几部分来讲:
数据预处理 严格来说,我并没有做多少数据预处理的工作。只是一些加载数据的函数,分别是 loadTrainSet()和loadTestSet(),加载训练数据集和测试数据集。代码如下,里面顺便做了归一化以及零均值(这里不做standardize差别也不大)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #load train set def loadTrainSet(): traindata = [] trainlabel = [] table = {"Class_1" :1 ,"Class_2" :2 ,"Class_3" :3 ,"Class_4" :4 ,"Class_5" :5 ,"Class_6" :6 ,"Class_7" :7 ,"Class_8" :8 ,"Class_9" :9 } with open ("train.csv" ) as f : rows = csv.reader(f ) rows .next () for row in rows : l = [] for i in range (1 ,94 ): l .append(int (row [i ])) traindata.append(l ) trainlabel.append(table .get (row [-1 ])) f .close () traindata = np.array (traindata,dtype="float" ) trainlabel = np.array (trainlabel,dtype="int" ) #Standardize(zero-mean,nomalization) mean = traindata.mean(axis=0 ) std = traindata.std (axis=0 ) traindata = (traindata - mean)/std #shuffle the data randomIndex = [i for i in xrange(len (trainlabel))] random.shuffle(randomIndex) traindata = traindata[randomIndex] trainlabel = trainlabel[randomIndex] return traindata,trainlabel #load test set def loadTestSet(): testdata = [] with open ("test.csv" ) as f : rows = csv.reader(f ) rows .next () for row in rows : l = [] for i in range (1 ,94 ): l .append(int (row [i ])) testdata.append(l ) f .close () testdata = np.array (testdata,dtype="float" ) #Standardize(zero-mean,nomalization) mean = testdata.mean(axis=0 ) std = testdata.std (axis=0 ) testdata = (testdata - mean)/std return testdata
代码挺长的,但其实都是些简单的读取和处理工作。(如果不想写这么繁琐的代码,可以试试数据分析包pandas,几行代码完成这些工作)。
模型评估 模型评估一般都是从训练数据集中分出一部分来做validation,一般我们是用validation accuracy来评估的(如上所述),但是这个比赛既然官方给出了评估公式,我们就根据这个公式写个评估函数:
1 2 3 4 5 6 7 8 9 10 #Evaluation function#Refer to:https:def evaluation (label,pred_label) : num = len (label) logloss = 0.0 for i in range (num) : p = max (min(pred_label[i][label[i]-1 ],1 -10 **(-15 ) ),10 **(-15 )) logloss += np.log (p) logloss = -1 *logloss/num return logloss
以上三个函数loadTrainSet()、loadTestSet()、evaluation()我都放在代码文件preprocess.py中,此外在该文件中还定义了一个saveResult()函数,根据test set的预测类别test_label生成一个用于提交结果的submission.csv文件。
分类算法 直接调用sklearn里面的随机森林,参数调节:随便调了几次决策树的数目,可以使logloss达到0.5左右,这个得分目前已经打败了一半的参赛队伍。
1 2 3 4 5 6 7 8 9 10 11 12 def rf (train_data,train_label,val_data,val_label,test_data,name="RandomForest_submission.csv" ) : print "Start training Random forest..." rfClf = RandomForestClassifier (n_estimators=400 ,n_jobs=-1 ) rfClf.fit (train_data,train_label) #evaluate on validation set val_pred_label = rfClf.predict_proba (val_data) logloss = preprocess.evaluation (val_label,val_pred_label) print "logloss of validation set:" ,logloss print "Start classify test set..." test_label = rfClf.predict_proba (test_data) preprocess.saveResult (test_label,filename = name)
这个函数的参数val_data就是验证集数据,从train.csv里面取出6000个构成,大约占1/10的数据。
这部分的代码放在文件RandomForest.py中。
有兴趣的去我github获取代码 试试吧。Have Fun!