清明三天假期眨眼就划过了,你的元气值恢复了吗,还是依然感觉疲惫?
(关注公众号:livandata,回复:元气值,即可获取本文案例的代码)
随着工作年限的增长,这一感觉似乎越来越明显,似乎一周七天里面最累的是周一——黑眼圈、惺忪眼、哈气连天,不自觉会想起曾经那个三天网吧,三天课堂的少年。
那么,如何才能预测出一个人的工作恢复能力呢?笔者借着小伙伴的调研,做了简单的探索。有兴趣的小伙伴可以简单试一下,刚好也借这个机会梳理一下常用的机器学习的常用算法,毕竟这个研究的过程,几乎尝试了常见的机器学习算法。
文章的缘起是一个心理学问题,希望能从微博近几个礼拜的数据中找寻到人类的情感波动,并辅以问卷调研的方法探索当下发微博的人的复工程度,由于数据的权限问题,这里只做技术性探讨,后文也会附上代码,如果有兴趣,欢迎大家尝试。
之前在《增长的旋律——AARRR模式思考(三)》文章中曾仔细分析过微博和微信的差异,作为一个线上的公共场所,微博无时无刻不承载着各种网络言论,其中不乏对工作的安排、对生活的情绪,微博也便成为了线上反应各自情绪的数据来源。那么,通过对微博数据的挖掘,能否预测出真实生活中人的行为和状态呢?作为一个数据人,总是希望能够有机会一探究竟,下面我们一起走一遍研究的过程:
P1 数据收集
研究随机圈定了一群经常使用微博的人,收集他们在微博上发表的博文,记录他们的发表时间,发表地点,点赞和评论次数等相关信息,每周为一个单位,持续关注三到四个月的时间,以此来探索被试者的情绪波动,确定被试者在何种情绪下会发出什么样的文字。
为了更全面的探索,研究增加了线下调研的部分,对这些经常使用微博的人进行有偿问卷作答,从工作状况、家庭状况、收入以及睡眠等几个方面设计问卷,并通过心理学的方法探索被试者在心理剥离、放松经验、控制经验等方面的得分,探索用分数的形式表现紧张、愤怒、精力、抑郁等多种情绪。
数据的收集结构如下图:
通过上面的两种思路,我们收集到了一整套完整的数据,涵盖了文本、字符串和数字,经过汇总整理后,我们得到如下表格:
(1)线下调研部分数据样式为:
(2)微博部分数据样式为:
P2 数据处理
观察上面的数据,我们会发现,数据的样式比较多样,有文本形式,有时间值,有数值得分也有字符串等,需要进行较为详细的特征工程,在建模的时候花费大量时间的往往就在这里了:离散值怎么向量化?文本怎么提取语义?怎么做降维处理?怎么归一化?基本步骤都会围绕这些问题进行,对于离散化、归一化和降维处理都是一些常规的做法,在sklearn中会有一些完整的函数:
1)离散化等one-hot处理:
这一处理方法主要应用在特征独立性要求不强的模型中,因为one-hot是将一个特征以向量的方式呈现成多列,这些扩展开的列存在一定的相关性,如果对于特征独立性要求高的模型可以尝试使用WOE计算方法,将各个无法计算的特征(【山东、上海、……】)转化成可以计算的数字。
另外为了便于分类,也会存在将连续的数字拆分成几个离散的值,比如将【1,2,3,4,5,6,7,8,9】等连续值拆分成两类【0,1】,这样的方法往往会用统计学中的一些分箱方法,进行等频分箱或者等额分箱等。
2)降维处理:
降维处理在建模过程中算是必备的一个步骤,经过离散化处理之后,数据特征有可能已经达到1000甚至10000维度,这一数据量直接投放到模型中往往会引发维度爆炸、维度灾难,那如何降维呢?最常用的方法主要有两个:一般机器学习的模型会比较常用主成分分析,神经网络深度学习部分主要是embedding方法,这些方法在降维的效果上不仅能够有效减少维度,更重要的他们能够减少各个特征中大量为零的状态,使模型的训练能更加高效精准。
这两种方法分别在两个包里:
主成分分析在sklearn中的函数为:
estimator = PCA(n_components=num)
pca_X_train = estimator.fit_transform(X_train4)
pca_X_test = estimator.fit_transform(X_test4)
pre_test_Y = y_test[['y_mean']]
pca_train_Y = y_train['y_mean'].tolist()
embedding在keras中的函数为:
model = Sequential()
model.add(Embedding(5000, 512,input_length=100))
讲到embedding不妨多说一句,在深度学习中,embedding、word2vec、dropout、attention被称作是必备组件,可见embedding的地位。
3)特征归一化:
经历过离散化、降维处理之后,有没有发现我们的特征有大有小,来自于各个方向的数据往往存在不同的量纲,直接投放到模型中会存在量级上的差异,千万级别的数据会轻而易举的抹杀掉个位数的特征,导致量级小的特征特性不明显,因此需要进行归一化处理。归一化主要是为了取消掉各个特征之间的量纲,使各个特征统一在一个维度使用。
归一化在sklearn中的函数为:
from sklearn import preprocessing
import numpy as np
X = np.array([[ 1., -1., 2.],
[ 2., 0., 0.],
[ 0., 1., -1.]])
X_scaled = preprocessing.scale(X)
归一化的方法除了我们常用的z-score标准化之外,还有min-max标准化,改进的z-score标准化等方法,使用过程中可以根据需要选择相应的方法。
上面的三个处理方法往往是我们建模过程中的标准套路,几乎可以不假思索的应用于所有的特征工程,看到这里的你有没有不解渴的感觉,费这么大功夫写一篇文章,难道连一点特产也没有吗?当然不是,这个模型构建的特色在于对文本的处理,也就是下面我们重点讲的东西:
文本的处理往往是独立于数据挖掘的另外一个领域——NLP,这一领域相对独立但又与模型有千丝万缕的联系,我们来看一下本文中的案例使用了什么独特的方法:
本文采用的方法主要是采用关键词处理的方法,根据博文进行词性分析,根据词性计算各个词片段的得分,进而得出各个关键词的权重分。个人以为,这也是这个研究中比较有特色的一个文本处理方法,不同于我们经常使用的TF-IDF、LDA、HMM隐马、CRF条件随机场等NLP模型。我们来仔细了解一下这一方法:
1)梳理关键词:整理出我们比较关注的关键词,添加到特征工程中,作为特征值,并同时筛选程度词和否定词,作为计算权重的依据;
2)切分词短句:按照微博号汇总每个被试者发表的博文,并将博文按照“,”,“。”等标点符号拆分成一个个的短句,此时每个被试者的博文就会成为一个个带有一定语义的词短句,我们接下来就是要通过对这些短句进行处理,来判断第一步中各个关键词的权重,并作为特征值将其融入到特征工程中。
3)统计关键词词频:遍历每一个词短句,统计其中关键词的频数,如果当下词短句有对应的关键词则记录词频数l1,如果没有对应的关键词,则记为0。
4)计算程度词权重:不同的程度词权重系数不同,比如:“百分之百”、“倍加”等记为2,“多么”、“格外”等记为1.75,“大不了”、“更加”等记为1.5,“多多少少”、“还”等记为0.5,如果没有程度词,则记为1,这样的词汇可以区分出每个被试者在博文中的情绪强弱,并以数字的形式表现出来。
5)计算否定词权重:如果词短句中出现“没有”、“未必”等词语时可以根据需要标记成-1或者-0.5,没有否定词则记为1,通过这个方法来减少词短句的权重。
6)计算关键词权重:基于上面的计算我们将关键词、程度词和否定词汇总在一起,形成我们对应关键词的权重值:
关键词权重=关键词词频+程度词权重*否定词权重
上文中各个词性的词语可以按照项目的需要进行相应的调整,没有统一的规则说是哪些词一定在哪个项下,也没有统一的规则确定每个词性的权重值,具体的数值还是需要在应用的场景中仔细琢磨。
案例如下:
被试者在微博中发表了一个博文:“不过今天工作累死了,下班好好休息一下,真不想起床呀,但愿明天工作不这么累”。
我们的分析步骤可以按照下面的流程一步步实现:
1)词短句切分:
首先我们把上面这个博文按照“,”、“。”切分成四个词短句,分别为:[S1:“不过今天工作累死了”,S2:“下班好好休息一下”,S3:“真不想起床呀”,S4:“但愿明天工作不这么累”]
2) 关键词计算:
遍历我们关注的关键词,统计这个被试者对关键词的使用频次:
下表中关键词记为被选为特征值的关键词,词短句记为含有该关键词的词短句,权重即为词短句出现的次数。
3) 程度词计算:
微博片段出现程度词“太”和“不过”,对应查询程度词表发现“太”的权重是2,“不过”的权重是1.75,所以,S1的程度词权重为:2+1.75=3.75;同理,S2的程度词权重为1,S3为1,S4为1。
4) 否定词计算:
词短句S1、S2中没有否定词,则否定词权重记为0,词短句S3有否定词“不”,则权重记为-1,S4中有否定词“不”,则权重记为-1
5) 汇总出关键词权重:
以此为例,同理可以计算出“下班”、“休息”、“起床”等关键词的权重值。
对应计算公式如下:
梳理出上面的代码,我们可以看到,越是在这种复杂的逻辑面前越体现python的简洁:
splitchar = [',', '.', '?', '!', ';', ',', '。', ';', '?', '!']
# 每个微博号一个t:
for r in range(0, len(data_com['com_contents'])):
t = data_com.loc[r, 'com_contents']
t_l = []
for j in splitchar:
t = t.replace(j, '=')
S = t.split('=')
# 对于每一个自变量关键词:
for i in data_col:
# 计算i中对应的片段有多少:
S1 = [x for x in S if(len(x.split(i)) > 1)]
print(i)
# 遍历各个S片段中哪些片段有i,权重记为1:
t_q_z = 0
for s in S1:
# 计算每个片段中的目标词,有次关键词的记为1
# len(s.split(i)) > 1:说明s片段中有关键词
# 1、计算目标值频次:
pic = len(s.split(i)) - 1 if(len(s.split(i)) > 1) else 0
# 2、计算程度词和否定词权重:
# 2.1)f1表示程度词权重为2:
q_f1 = sum([len(s.split(a)) - 1 for a in f1])
q_f11 = 1 if(q_f1 == 0) else q_f1 + 2
# 2.2)f2表示程度词权重为0.5:
q_f2 = sum([len(s.split(a)) - 1 for a in f2])
q_f21 = 1 if (q_f2 == 0) else q_f2 + 0.5
# 2.3)f3表示程度词权重为1.5:
q_f3 = sum([len(s.split(a)) - 1 for a in f3])
q_f31 = 1 if (q_f3 == 0) else q_f3 + 1.5
# 2.4)f4表示程度词权重为1.75:
q_f4 = sum([len(s.split(a)) - 1 for a in f4])
q_f41 = 1 if (q_f4 == 0) else q_f4 + 1.75
q_f = q_f11 + q_f21 + q_f31 + q_f41
# # 2.5)否定词计算:
fd_f = sum([len(s.split(b)) - 1 for b in f5]) * (-1)
# 3)s的权重词:
qz = pic +(q_f * fd_f)
t_q_z = t_q_z + qz
data_c.loc[r, i] = t_q_z
介绍到这里,大家有没有一些想法:所谓NLP,简单讲即为区分各个词语词性的情况下,深度计算各个词语之间的组合概率,寻找最大组合概率的词语序列,并解读其中的含义。案例中的这个方法有没有这一思路的影子呢?笔者认为是有的,所以,笔者在与小伙伴聊天的时候曾戏虐的说是初探NLP,也不算是一句妄言了。
关键词的处理,除了上面直观计算外还有一些比较简单的分词工具,可以让大家既快捷又准确的切分各个关键词,并计算对应的词频、词性等,笔者了解的一个工具为——文心系统。是一个心理学领域的文本分析软件,可以有效的切分各个关键词并进行词性计算。
如下即为文心系统的界面,操作较为简单,效果也非常明显,不过问题在于只有win版的,苹果系统无法使用这个软件,略感可惜。
经过上面特征工程的部分,我们的特征处理可以说基本上处理完成了,接下来的部分,我们介绍一下在这个案例中使用的一些常用算法。由于前期对数据特征的精细化处理,研究中并没有选用一些较为高深的算法模型,笔者相信:数据决定了结果的上限,而算法只是让我们的结果无限逼近这个上限而已。
P3 模型构建
这次的研究我们尽可能的遍历了常用的几个算法,以求找到最佳的效果,确定最优的模型,结果表明:天下没有免费的午餐,没有哪个算法是有天然的优势的,具体算法的好坏,还是需要根据项目中的特征进行筛选比较的,或许让你的模型更上一层楼的是一个你一直看不上的简单算法,本案例就给笔者好好的上了一课。
本案例中,笔者使用了SVM、岭回归、弹性网、CART、LR、GBDT、LASSO甚至使用GBDT+Ridge等融合模型,效果最好的不是这些貌似高大上的GBDT等算法,也不是理论基础扎实的GBDT+Ridge等融合模型,而是一个不起眼的岭回归模型Ridge。
接下来我们详细描述一下研究的过程。
1)汇总特征工程:
经过上面的特征处理,我们得到了一个比较完成的特征工程集合,其中不仅涵盖了关键词部分,也涵盖了数值部分,并在此基础上实现了降维处理,得到的特征工程如下:
……
如上只展示数据的样式,研究数据保密的原因无法全部呈现,请谅解~
2)进行连续值预测:
主要是将复工能力看做1-100的连续值,用连续值预测的模型训练相应的结果,研究选用的连续性模型主要有岭回归、线性回归、弹性网、CART树等常用模型,由于sklearn良好的封装效果,我们在使用这些模型的时候只需要进行函数调用即可。
连续型模型构建的效果检验我们使用的是修正的R方检验,越靠近1的效果越好,另外,在效果评估过程中,笔者曾用最简单的相关系数进行效果检验,可以初步判断效果,但最终还是要落地到R方的评估方法上来。
3)进行离散值预测:
连续型预测之后我们又尝试用离散的形式进行模型构建,将y值进行二分处理,分别表示复工能力较高和复工能力较低两类,训练使用的模型为:SVM、LR回归、GBDT等分类模型,训练效果方面我们尝试用最常见的几种:精准率、召回率和F1-score检验三个方法,f1值效果较为明显,究其原因,f1检验是综合了精准率和召回率的模型,能够很好的进行效果评估。
如下为代码部分:
经过这一系列的训练,我们会得出对当下被试人群的预测值,用修正的R2进行检验发现:R2在0.4~0.5之间,证明这一方法在复工能力预测方面有一定的价值,也验证了通过微博的情感分析来判断人在实际生活中的行为有一定的效果,为行为分析提供了可能性。