听完smp比赛各队伍的技术分享后, 回来实验室后用了一周的时间去复刻第一名,第二名关于任务二的做法.任务二的研究对象为csdn技术论坛的用户,根据他们的博客行为和博客内容,以及用户与用户之间的关系,分析用户的主要兴趣点。比赛给定的兴趣标签空间为42个兴趣类别,兴趣类别之间呈现明显的不平衡分布。在这个过程中, 我学习到了非常多的东西. 不过遗憾的是, 到最后也无法达到他们训练出来的效果, 估计某些地方的特征构造和参数调整方面有问题.
先说第一名的方法, 其主要是利用stacking的思想, 做了两层模型的预测. 第一层先对每个类别做了各自的逻辑回归预测, 通过10-折校验方法产生了10个不同的逻辑回归模型, 加上不同的类别,一共产生了420个逻辑模型, 对用户的特征向量输入(word2vec, doc2vec)分别了做不同类别的逻辑回归预测然后取平均, 最终得到了42维的概率特征向量.做一个图也许比较容易理解:
在第二层中, 利用42维的概率特征向量,构建三层神经网络模型, 输出模型用softmax函数.而三层神经网络也是用了10-折模型做平均输出.
由于比赛的时候, 我是采用了lsi模型训练出博客的向量表示, 所以我直接用这个向量作为原始向量输入, 但发现第二层常常出现过拟合的情况(即都预测大类), 后来将神经网络改为了随机森林, 模型预测为的准确率为0.412,效果不太理想.
第二名的方法十分简洁有效, 该方法直接用了原始的向量(lda向量)做三层神经网络模型, 没有交叉验证.我直接套用他的思想, 用keras训练神经网络模型, 200维的lsi博客向量作原始输入, 预测的结果极其有效,一下子就0.438, 超过我当时比赛的最好水平(0.434),真是感叹自己的见识少得离谱.
这里附上, 我当时是如何训练lsi模型的代码, 以及后期训练三层神经网络模型和预测用户兴趣.
def train_lsi_model(blog_content_list):
documents = blog_content_list ## 每篇博客, 以及做好分词,去除无关标点符号
texts = [[word for word in document] for document in documents] ## 将博客构建列表形式
dictonary = corpora.Dictionary(texts) ## 统计博客的出现的词项
dictonary.save('../dictionary.model')
corpus = [dictonary.doc2bow(text) for text in texts] ## 将博客转化为BOW形式
tfidf = models.TfidfModel(corpus) ## 提炼tfidf模型
tfidf.save('../tfidf.model')
corpus_tfidf = tfidf[corpus]
corpus_tfidf.save('../corpus_tfidf.model')
lsi = models.LsiModel(corpus_tfidf, id2word=dictonary,num_topics=num_topics) ## 训练lsi模型, 隐式主题为num_topics
lsi.save('../lsi.model')
corpus_lsi = lsi[corpus_tfidf]
corpus_lsi.save('../corpus_lsi.model')
index = similarities.MatrixSimilarity(lsi[corpus]) ## 用于计算博客相似度
index.save('../index.model')
blog_content = open(blog_content_file).read().lower().strip().split('/')
query_bow = dictonary.doc2bow(blog_content) ## 转化为bow模型
query_lsi = lsi[query_bow]
from keras.models import Sequential
from keras.layers.core import Dense, Activation, Dropout
from keras.utils import np_utils
## one_hot映射
def one_hot_encode_object_array(arr):
uniques, ids = np.unique(arr, return_inverse=True)
return np_utils.to_categorical(ids, len(uniques))
def network_train(X, y): ## X为特征向量, y为目标属性
train_y_ohe = one_hot_encode_object_array(y)
model = Sequential()
model.add(Dense(60, input_shape=(200,)))
model.add(Activation('sigmoid'))
model.add(Dropout(0.02)) ## 避免过拟合
model.add(Dense(42))
model.add(Activation('softmax'))
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=["accuracy"])
model.fit(X, train_y_ohe, nb_epoch=100, batch_size=1, verbose=1)
return model
def predict(X, model):
predict_y = []
result = model.predict_proba(X) ## 预测概率
for i in range(0,result.__len__(), 1):
result[i] = np.array(result[i])
topk = result[i][np.argpartition(result[i],-3)[-3:]] ## 取概率最大的前三个标签
predict = []
for j in range(0,42,1):
if result[i][j] in topk:
predict.append(j)
predict_y.append(predict)
return predict_y