最近被分配到商业分析组配合商业分析师对流失掉的客户进行研究。我最先接到的任务是根据客服部门记录的客户的流失原因,对于这些客户的流失原因做分类。商业分析师给我提供了23个类别,要求我把客户都分到这些类中。最开始我企图通过建立关键词规则,比如包含某些单词或者不包含某些单词,但是实际上发现分类的结果很差,规则首先不完备,并且彼此还可能冲突,分类的结果当然就很差。于是我就想到可以利用文本挖掘的方法,对于客户流失的原因进行分类。之前对于机器学习的理论学了不少,但是真正拿实际的数据出来构建一个机器学习模型还是第一次,所以想记录一下这整个过程。
首先,尽管我没做过文本挖掘,但是我大致知道这其中的原理。要做机器学习,必须有输入,输入就是通过将文本进行分词,然后构建词典。本来我是想用朴素贝叶斯的,但是发现效果很差。首先通过jieba分词将每个用户流失原因进行分词,然后将所有词语加在一起,去重,去掉电话号码,还有符号,保存起来,作为我的词典。
有了词典,就需要对于每个用户的流失原因文本进行分词,并且按照词典转化成向量。在最开始摸索的时候,我还是用一个超大的for循环进行的。后来发现sklearn自带转化向量的工具。但是它的分词并不符合中文的逻辑,所以我用了一个我觉得依照我现有知识能做的最机智的事儿。我将每个文本利用jieba分词,然后词语之间用','隔开。这样sklearn就会按照中文的逻辑进行分词,于是我的输入就秒有了。
接下来,我对于数据集进行人工或者简单的标注,然后通过决策树学习,并且预测一部分,人工的将分类错误的进行修改,再利用数据集进行学习,再分类再人工纠错,这样,进行几次之后我就拥有一个基本完全标注正确的50000行的数据。我太机智了,我估计别人可能也是在这么做,但是我之前并不知道,我只能说,在现有条件下,我做了我最多能做的了。最后发现在测试集上有97%左右的正确率。哈哈哈哈哈哈哈哈哈
import pandas as pd
import numpy as np
import jieba
import sklearn.cross_validation as skc
from sklearn import tree
import jieba
from sklearn.feature_extraction.text import CountVectorizer
class creatdic:
def __init__(self,data):
assert 'reason' in data.columns,print('Enter the right data')
self._df = data.copy()
self._df.loc[self._df[pd.isnull(self._df['reason'])].index,'reason'] = 'None'
def _creatdic(self,sentence):
sents = jieba.cut(sentence)
sent = np.array([x for x in sents])
return np.unique(sent)
def creat_dic(self):
temp = self._df.reason.apply(self._creatdic)
dic = list()
for word in temp:
for j in word:
if j not in dic:
dic.append(j)
else:
continue
for i in range(len(dic)):
try:
float(dic[i])
dic.pop(i)
except:
continue
dictionary = pd.DataFrame(pd.Series(dic),columns=['word'])
return dictionary
class creatX:
def __init__(self,data):
self._df = data
def _creatdic(self,name):
words = jieba.cut(name)
word = np.array([x for x in words if len(x) > 1])
return np.unique(word)
def _concat(self,lis_words):
temp = str()
for i in range(len(lis_words)):
if i == 0:
temp = lis_words[i]
else:
temp = temp + ',' + lis_words[i]
return temp
def creatX(self):
X = self._df.reason
X1 = self._df.reason.apply(self._creatdic)
X2 = X1.apply(self._concat)
return X2
class TreeClassifier:
def __init__(self,data):
assert 'reason' in data.columns and 'Label' in data.columns,print('The data is not vailid')
self._df = data
def _creat_trainX(self,df):
t_set = df
dlabel = t_set[pd.isnull(t_set['reason'])].index
t_set = t_set.drop(dlabel,axis = 0)
cx = creatX(t_set)
tX = cx.creatX()
df = pd.DataFrame(tX,columns=['reason'])
df['Label'] = t_set['Label']
return df
def predict(self,pre_data):
train = self._creat_trainX(self._df)
dic = pd.read_excel('words.xlsx')
dic2 = dic.word
trainX,testX,trainy,testy = skc.train_test_split(train['reason'],train['Label'])
vect = CountVectorizer(vocabulary = dic2).fit(trainX)
trainX,testX,trainy,testy = skc.train_test_split(train['reason'],train['Label'])
vect = CountVectorizer(vocabulary = dic2).fit(trainX)
X_train_vectorized = vect.transform(trainX)
X_train_vectorized
t2 = tree.DecisionTreeClassifier()
t2.fit(X_train_vectorized,trainy)
predata1 = pre_data.copy()
predf = self._creat_trainX(predata1)
predf['Label'] = t2.predict(vect.transform(predf.reason))
predata1['label'] = predf['Label']
return predata1
决策树本身没什么太多调试,基本用默认的参数分类的结果就很好,主要困难集中在数据集的标注和反复学习,这是特别费工夫的。并且自己构建的字典对于准确率的影响也比较大。