简介
几乎所有人都会购物。从基本的必需品(比如食品)到娱乐产品(比如音乐专辑),我们会购买各种各样的物品。当购物时,我们不仅会寻找在生活中用到的东西,也会在表达我们对某些社会群体的兴趣。我们的在线行为和决策塑造了我们自己的行为特征。
当购买产品时,该产品具有多个属性,这使得它类似或不同于其他产品。例如,一个产品的价格、大小或类型都是它的不同特征。除了这些数值或枚举类的结构化属性之外,还有非结构化的文本属性。例如,产品描述或客户评论的文本也构成了其明显的特征。
对于从这些非结构化文本属性中提取有意义的东西而言,文本分析和其他自然语言处理(NLP)技术非常有帮助,而这对行为分析等任务又很有价值。
本文将介绍如何使用文本分类来构建行为描述模型。文中将展示如何使用 SciKit 这个强大的基于 Python 的机器学习包来实现模型构造和评估,还会对模拟的客户及其产品购买历史记录应用该模型。在这种特定的场景中,将会构造一个模型,向客户分配一些音乐听众感兴趣的特色内容,比如狂欢、哥特或金属音乐。该分配是以每个客户的购买的具体产品和相应的文本产品说明为基础的。
音乐行为描述场景
请看下面的场景。您有一个包含许多客户个人资料的数据集。每个客户个人资料都包括客户已经购买的所有产品的一个简洁的、基于自然语言的描述列表。下面是一款靴子的示例产品描述。
描述:这一款男装搭扣靴子是一双哥特式靴子,具有暗波纹亚文化气息,靴子的铆钉头带来了业内的最新时尚。这款靴子采用了合成的人造皮革鞋面,鞋带正面使用的是交叉扣,这种交叉一直延续到了鞋统,鞋底是橡胶大底,底部采用了花纹底,而前帮则采用了战斗风格,内侧配有拉链,方便穿鞋和脱鞋。鞋统 13.5 英寸,腿部开口周长约 16 英寸。(鞋码为 9.5。)风格:男装搭扣靴子。
我们的目标是根据这些产品描述,将每位当前用户和未来用户分类到某个行为配置文件中。
如下所示,负责人使用产品示例来建立行为特征、行为模型、客户特征,以及最终的客户行为特征。
图 1. 构建客户行为特征的高层次方法
第一步是假设负责人的作用,并向系统提供对每个行为特征的理解。实现此操作的一种方法是手动将每个产品的示例放入系统。示例有助于定义行为特征。本次讨论将用户划分到以下音乐行为描述之一:
朋克
哥特
嘻哈
金属
狂欢
向定义为朋克的产品提供示例,比如朋克专辑和乐队的描述,例如,Sex Pistols 的 "Never Mind the Bollocks"。其他项目可能包括与发型或鞋类相关的产品,比如鸡冠头和 Doc Marten 皮靴。
库、软件和数据的建立
这篇文章中所使用的全部数据和源代码都可以从 bpro project on JazzHub 下载。在下载并解压 tar 文件后,需要确保您拥有 Python,SciKit Learn(机器学习和文本分析包),以及所有的依赖关系(比如 numpy、scipy,等等)。如果使用的是 Mac,那么 SciPy Superpack 可能是您最好的选择。
在解压 tar 文件后,您会注意到两个包含简介数据的 YAML 文件。产品描述是通过读取种子语料(或文档的正文)来人工生成的。在生成产品描述的过程中,会考虑到词语在产品描述中出现的频率。清单 1 是一个人工的产品描述。
备注:下面的描述并不是一个真正的自然语言描述,但在实际情况中可能会出现这种描述。
清单 1. 人工的产品描述
customer single clothes for his size them 1978 course group
rhymes have master record-breaking group few starts heard
blue ending company that the band the music packaged
master kilmister not trousers got cult albums heart
commentary cut 20.85 tour...
这个分析包括两个数据文件:
customers.yaml:包括一个客户列表。对于每个客户,包括一个产品描述列表,以及目标标签,或正确的 行为描述。正确的行为描述是指您知道的那个行为描述是正确的。例如,在实际的场景中,将会检查哥特用户的特征数据,以便验证这些购买行为表明该用户是一个哥特用户。
behavioral_profiles.yaml:包含描述文件(朋克、哥特等)的列表,以及定义该描述文件的产品描述的样本集。
您可以通过运行命令 python bpro.py -g 生成自己的模拟文件。
备注:必须先在种子目录中填充一些内容,定义感兴趣的流派。进入种子目录,打开任何文件,并了解相关说明。您可以操纵 bpro.py 文件中的参数,以改变产品描述长度、噪声量、训练示例的数量或其他参数。
构建行为描述模型
首先,使用 SciKit 的 CountVectorizer 构建一个基于术语计数的简单语料库描述。语料库对象是包含产品描述的一个简单字符串列表。
清单 2. 构建一个简单的术语计数
vectorizer = CountVectorizer(gmin_df=1)
corpus=[]
for bp in behavioral_profiles:
for pd in bp.product_descriptions:
corpus.append(pd.description)
SciKit 还有其他更先进的矢量器(vectorizers),比如 TFIDFVectorizer,它使用术语频率/逆文档频率 (TF/IDF) 加权来存储文档术语。TF/IDF 表示有助于让独特的术语(比如 Ozzy、 raver和 Bauhaus)的权重比反复出现的术语(比如 and、 the 和 for)的权重还要高。
接下来,将产品描述划分为单个单词,并建立一个术语字典。分析器在匹配过程中找到的每个术语被赋予一个与在结果矩阵中的列相对应的惟一整数索引:
fit_corpus = vectorizer.fit_transform(corpus)
备注:这个分词器配置(tokenizer configuration)也丢弃了单字符单词。
您可以使用 print vectorizer.get_feature_names()[200:210] 打印出一些特性,看看哪些单词被分词。此命令的输出如下所示。
清单 3. print 命令的输出
[u'better', u'between', u'beyond', u'biafra', u'big',
u'bigger', u'bill', u'billboard', u'bites', u'biting']
请注意,当前矢量器没有词干化的单词。词干化 是为词尾变化或派生的单词得到一个共同的基础或词根形式的过程。例如,big 是在前面列表中的 bigger 的一个常见词干。SciKit 不处理更复杂的分词(比如词干化、词簇化和复合断词),但您可以使用自定义分词器,比如那些来自 Natural Language Toolkit (NLTK) 库的那些分词器。关于自定义分词器的示例,请参见 scikit-learn.org。
分词过程(比如,词干化)有助于减少所需的训练实例的数量,因为如果某个单词有多种形式,而且不要求对每种形式都提供统计表示。您可以使用其他技巧来减少培训需求,比如使用类型字典。例如,如果您有所有哥特乐队的乐队名称列表,那么可以创建一个共同的文字标记,比如 goth_band,并在生成特性之前将它添加到您的描述中。通过使用这种方法,如果在描述中第一次遇到某个乐队,该模型处理此乐队的方式会与处理模型可以理解其模式的其他乐队的方式相同。对于本文中的模拟数据,我们要关心的不是减少培训需求,所以我们应该继续执行下一个步骤。
在机器学习中,出现这样的监督分类问题是因为首先要为一组观察定义一组特性和相应的目标,或者正确的标签。然后,所选择的算法会尝试相应的模型,该模型会找到最适合的数据,并且参照已知的数据集来最大限度地减少错误。因此,我们的下一步操作是构建特性和目标标签矢量(参见清单 4)。随机化观察总是一个好办法,因为它可以防止验证技术没有这样做。
清单 4. 构建特性和目标标签矢量
data_target_tuples=[ ]
for bp in behavioral_profiles:
for pd in bp.product_descriptions:
data_target_tuples.append((bp.type, pd.description))
shuffle(data_target_tuples)
接下来,组装矢量,如清单 5 所示。
清单 5. 组装矢量
X_data=[ ]
y_target=[ ]
for t in data_target_tuples:
v = vectorizer.transform([t[1]]).toarray()[0]
X_data.append(v)
y_target.append(t[0])
X_data=np.asarray(X_data)
y_target=np.asarray(y_target)
现在,您可以选择一个分类器并修整您的行为描述模型。在此之前,最好先评估模型,这样做只是为了确保该模型可用,然后再让客户试用。
评估行为描述模型
首先使用 Linear Support Vector Machine (SVM),对于此类稀疏矢量问题,这是一个匹配度很高的不错的模型。使用代码 linear_svm_classifier = SVC(kernel="linear", C=0.025)。
备注:您可以通过修改这个模式初始化代码来切换到其他模型类型。如果需要试用不同的模型类型,那么可以使用这个分类器映射,它为一些常见的选项设置了初始化。
清单 6. 使用分类器的映射
classifier_map = dict()
classifier_map["Nearest Neighbors"]=KNeighborsClassifier(3)
classifier_map["Linear SVM"]=SVC(kernel="linear", C=0.025)
classifier_map["RBF SVM"]= SVC(gamma=2, C=1)
classifier_map["Decision Tree"]=DecisionTreeClassifier(max
_depth=5)
classifier_map["Random Forest"]=RandomForestClassifier
(max_depth=5, n_estimators=10, max_features=1)
classifier_map["AdaBoost"]=AdaBoostClassifier()
classifier_map["Naive Bayes"]=GaussianNB()
classifier_map["LDA"]=LDA()
classifier_map["QDA"]=QDA()
因为这是一个多级分类问题(也就是说,在该问题中,您需要选择的可能类别多于两个),您还需要指定相应的策略。一种常见的方法是执行一对全的分类。例如,来自 goth 类的产品描述被用于定义一个类,而另一个类包括来自其他所有类( metal、rave,等等)的示例描述。最后,作为验证的一部分,您需要确保修整该模型的数据不是测试数据。一个常见的技术是使用交叉折叠验证法。您可以使用此技术五次,这意味着穿过数据的五个部分的分区五次。在每次穿过时,五分之四的数据被用于修整,其余五分之一用于测试。
清单 7. 交叉折叠验证
scores = cross_validation.cross_val_score(OneVsRestClassifier
(linear_svm_classifier), X_data, y_target, cv=2)
print("Accuracy using %s:%0.2f (+/- %0.2f) and %d folds"
% ("Linear SVM", scores.mean(), scores.std() * 2, 5))
尽管如此,您仍会得到完全精确的结果,这标志着模拟数据有点过于完美。当然,在现实生活中,始终会有干扰因素,因为群体之间的完美界限并不总是存在。例如,有 goth punk 的问题流派,所以像 Crimson Scarlet 这样的乐队可能会同时进入 goth 和 punk 的训练示例。您可以试一下 bpro 下载软件包 中的种子数据,以便更好地了解这种类型的干扰因素。
在了解一个行为描述模型之后,您可以再绕回来,用您的所有数据修整它。
清单 8. 修整行为描述模型
behavioral_profiler = SVC(kernel="linear", C=0.025)
behavioral_profiler.fit(X_data, y_target)
试用行为模型
现在,您可以玩一下模型,键入一些虚构的产品描述,看看模型如何工作。
清单 9. 试用模型
print behavioral_profiler.predict(vectorizer.transform(['Some black
Bauhaus shoes to go with your Joy Division hand bag']).toarray()[0])
请注意,它的确会返回 ['goth']。如果删除单词 Bauhaus 并重新运行,您可能会注意到,它现在会返回 ['punk']。
对您的客户应用行为模型
继续将修整过的模型应用于客户及其购买的产品描述。
清单 10. 将修整过的模型应用于我们的客户及其产品描述
predicted_profiles=[ ]
ground_truth=[ ]
for c in customers:
customer_prod_descs = ' '.join(p.description for p in
c.product_descriptions)
predicted = behavioral_profiler.predict(vectorizer
.transform([customer_product_descriptions]).toarray()[0])
predicted_profiles.append(predicted[0])
ground_truth.append(c.type)
print "Customer %d, known to be %s, was predicted to
be %s" % (c.id,c.type,predicted[0])
最后,计算准确性,看看您可以多频繁地分析购物者。
清单 11. 计算准确性
a=[x1==y1 for x1, y1 in zip(predicted_profiles,ground_truth)]
accuracy=float(sum(a))/len(a)
print "Percent Profiled Correctly %.2f" % accuracy
如果使用所提供的默认描述数据,结果应该是 95%。如果这是真实的数据,那么这是一个相当不错的准确率。
扩展模型
现在,我们已经构建和测试了模型,可以把它应用于数以百万计的客户个人资料。您可以使用 MapReduce 框架,并将修整后的行为分析器发送到工作节点。然后,每个工作节点都会得到一批客户个人资料及其购买历史,并应用模型。保存结果。此时,模型已被应用,您的客户被分配为一个行为描述。您可以在很多方面使用该行为描述分配任务。例如,您可能决定用定制的促销活动来定位目标客户,或者使用行为描述作为产品推荐系统的输入。
更多信息请查看IT技术专栏