Kaggle比赛-Otto Group Product Classification-打败一半参赛队伍的简单解法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012162613/article/details/44601523

简介

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个特征,参赛者的任务是对这些样本分类。



提交结果的格式

官网上给出的格式:

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差别也不大)。

#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来评估的(如上所述),但是这个比赛既然官方给出了评估公式,我们就根据这个公式写个评估函数:

#Evaluation function
#Refer to:https://www.kaggle.com/c/otto-group-product-classification-challenge/details/evaluation
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左右,这个得分目前已经打败了一半的参赛队伍。

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!

============

by wepon

展开阅读全文

没有更多推荐了,返回首页