精华内容
下载资源
问答
  • 数据集为用户评论业务分类数据(包括了训练集和测试集),根据评论所属业务进行分类,其中部分文本长度较
  • 对于短文本分类来说,更倾向于保留文本中的核心词,对于长文本来说去掉停用词更合适,原因是短文本本来词语就少,类目没有指证作用的词反而会预测值产生较大影响,保留核心词的最大弊端恐怕就是(1)核心词一词...

    d62976fdd9fa7e48e8cc8c4ec06cae4d.png

    好,今天我们继续说核心词抽取。核心词与停用词就是从两个方向上来减少参数量。对于短文本分类来说,更倾向于保留文本中的核心词,对于长文本来说去掉停用词更合适,原因是短文本本来词语就少,对类目没有指证作用的词反而会对预测值产生较大影响,保留核心词的最大弊端恐怕就是(1)核心词一词多义(2)一个核心词在多个类目中出现(3)一个短文本含有多个属于不同类目的核心词。而对于长文本来说,由于它有多个词,因此一些干扰词的干扰程度就会下降,因为核心词肯定是高频词,由于文本长,高频词也就占了优势。但并非长文本好预测短文本不好预测。短文本只有一个核心词且只属于一个类目,那也很好预测。长文本,含有多个类目的核心高频词,同样不好预测。

    进入今天正题,我们标注好新语料以后,或者说你爬取了适合于你的类目体系的某个类目的数据后,如何评价它的好坏呢,关键词抽取是一定的。而这里恐怕最好用的方法应该是统计高频词法,特别是当你的每条文本都很短的时候,当然如果你的每条文本都很长你可以用TF-IDF法。抽取出关键词以后你要怎么评价这些数据是否合适呢。我想应该有两种方法,方法1就是人工去看,同时过滤掉没用的词(首选)。方法2就是,在你已经有了一些该类目的数据或者有该类目的关键词以后,计算你的关键词序列与类目数据或者类目关键词之间的相似程度。

    这篇文章介绍的就是在获得短文本集或者长短混合的文本集后,统计频次抽取关键词(核心词)。如果你用HanLP的话,HanLP里有基于词频的关键词抽取API,但是它设置分词器只能设置Segment类及其词类的实例,而我需要实词分词,所以统计词频还是自己写了。代码如下:

    # coding:utf-8
    """
    基于词频统计法抽取单一类目关键词
    """
    
    import codecs
    from CommonPreprocess import preprocess
    
    word_freq = dict()
    
    topx = 100
    with codecs.open('../data/train/二期增量语料/20191202-文本分类训练语料.txt', 'rb', 'utf-8', 'ignore') as infile:
        for line in infile:
            line = line.strip()
            if line:
                word_li = preprocess(line)
                for word in word_li:
                    if word in word_freq:
                        word_freq[word] += 1
                    else:
                        word_freq[word] = 1
    
    # 按照频次降序的topx
    sorted_word_li = sorted(word_freq.items(), key=lambda x:x[1], reverse=True)
    print(u','.join([w[0] for w in sorted_word_li[:topx]]))
    
    # 按照词语长度降序的topx
    sorted_word_li = sorted(sorted_word_li, key=lambda x:len(x[0]), reverse=True)
    print(u','.join([w[0] for w in sorted_word_li[:topx]]))
    

    其中CommonPreprocess如下

    #coding:utf-8
    """
    通用预处理
    """
    from pyhanlp import *
    from nltk import ngrams
    
    # 加载实词分词器 参考https://github.com/hankcs/pyhanlp/blob/master/tests/demos/demo_notional_tokenizer.py
    Term = JClass("com.hankcs.hanlp.seg.common.Term")
    NotionalTokenizer = JClass("com.hankcs.hanlp.tokenizer.NotionalTokenizer")
    
    
    # 通用预处理(训练语料和预测语料通用)
    def preprocess(text):
        # 全部字母转小写
        text =text.lower()
        word_li = []
    
        #  NotionalTokenizer.segment中有去除停用词的操作
        for term in NotionalTokenizer.segment(text):
            word = str(term.word)
            pos = str(term.nature)
            # 去掉时间词
            if pos == u't':
                continue
            # 去掉字母数字词
            if pos == u'nx':
                continue
            # 去掉单字词(这样的词的出现有可能是因为分词系统未登录词导致的)
            if len(word) == 1:
                continue
            word_li.append(word)
    
        return word_li
    
    if __name__ == "__main__":
        while True:
            text = input("请输入待预处理文本:n")
            if text == u'q':
                break
            print(preprocess(text))
    

    抽取出核心词的目的就是为了在预测之前只保留句子中的核心词,如果你的文本短,或者你对精度有很高的要求,那么整理出核心词表来是必要的。用高频词表示核心词是对于通用文本比如微博之类的,如果你是电商之类的某一领域,那么特定领域的实体识别恐怕是你构建核心词表的关键方法。比如属性词、品牌词、物品词。对于实体识别,我个人认为也应该作为一种语料构建的算法,而不是作为一种实时预测的服务。

    好,今天的小工具介绍完毕,我要去看“心灵法医”喽。

    展开全文
  • 如果要使用聚类分析算法一堆文本分类,关键要解决这几个问题: 如何衡量两个对象是否相似 算法的性能怎么度量 如何确定分类的个数或聚类结束的条件 选择哪种分类算法 下面就带着这几个问题,以我工作中的一个...

    聚类分析是一种无监督机器学习(训练样本的标记信息是未知的)算法,它的目标是将相似的对象归到同一个簇中,将不相似的对象归到不同的簇中。如果要使用聚类分析算法对一堆文本分类,关键要解决这几个问题:

    • 如何衡量两个对象是否相似
    • 算法的性能怎么度量
    • 如何确定分类的个数或聚类结束的条件
    • 选择哪种分类算法

     下面就带着这几个问题,以我工作中的一个业务需求为例,来学习一下怎么对中文文本进行聚类。(此文略长,包含了理论基础、算法院里、代码实现和实验效果分析

    一. 业务需求

    我在工作中遇到这样一个需求:有个铁路通信专业的业务系统,收集了一些通信设备的生产厂商信息,共4500多条。由于数据是人工录入的,非常不规范,存在数据重复、信息不规范、错别字等现象,需要对数据进行分析归类,将相同或相似的数据划到一起,再通过人工审核规范数据,最终形成规范的字典数据。


    二、理论基础

    1. 相似度计算

    • 欧氏距离

    欧氏距离是一种常用的距离定义,指在m维空间中两个点之间的真实距离,对多维向量A=(A1,A2,……,An),B=(B1,B2,……,Bn),欧氏距离的计算公式如下:

     

    • 余弦相似度

    余弦相似度用向量空间中两个向量夹角的余弦值作为衡量两个个体差异的大小。相比欧氏距离度量,余弦相似度更加注重两个向量在方向上的差异,而非距离或长度上的差异。余弦值的计算公式如下:

    相对于欧氏距离,余弦相似度更适合计算文本的相似度。首先将文本转换为权值向量,通过计算两个向量的夹角余弦值,就可以评估他们的相似度。余弦值的范围在[-1,1]之间,值越趋近于1,代表两个向量方向越接近;越趋近于-1,代表他们的方向越相反。为了方便聚类分析,我们将余弦值做归一化处理,将其转换到[0,1]之间,并且值越小距离越近。

    2. 性能度量

    在选择聚类算法之前,首先来了解什么样的聚类结果是比较好的。我们希望同一个簇内的样本尽可能相似,不同簇的样本尽可能不同,也就是说聚类结果的“簇内相似度”高且“簇间相似度”低。

    考虑聚类结果的簇划分C={\left \{ C_{1},C_{2}, ...,C_{K}\right \}}, 定义:

    avg(C)=\frac{2}{(|C|(|C|-1))}\sum_{1\leq i< j\leq \left | C \right |}dist(x_i,x_j)

    diamC=max_{1\leq i< j\leq \left | C \right |}dist(x_{i},x_{j})

    d_{min} (C_i,C_j )= min_{x_i\in C_i,x_j\in C_j} dist(x_i,x_j)

    d_{cen} (C_i,C_j )=dist(\mu _i,\mu_j)

    其中,\mu代表簇C的中心点;avg(C) 代表簇C内样本的平均距离;diamC代表簇C内样本间的最远距离;d_{min} (C_i,C_j )对应于簇C_iC_j簇最近样本间的距离;d_{cen} (C_i,C_j )对应于簇C_iC_j 中心点间的距离。基于以上公式可导出下面两个常用的聚类性能度量内部指标:

    • DB指数(Davies-Bouldin Index,简称DBI)

    DBI=\frac{1}{k}\sum_{i=1}^{k}max_{j\neq i}\left ( \frac{avg(C_i)+avg(C_j)}{d_{cen}(C_i,C_j)} \right )

    • Dumn指数(Dumn Index,简称DI)

    DI=min_{1\leqslant i\leq k}\left \{ min_{j\neq i}\left ( \frac{d_{min}(C_i,C_j)}{max_{1\leqslant l\leq k}diam(C_l)} \right ) \right \}

    DB指数的计算方法是任意两个簇内样本的平均距离之和除以两个簇的中心点距离,并取最大值,DBI的值越小,意味着簇内距离越小,同时簇间的距离越大;Dumn指数的计算方法是任意两个簇的最近样本间的距离除以簇内样本的最远距离的最大值,并取最小值,DI的值越大,意味着簇间距离大而簇内距离小。因此,DBI的值越小,同时DI的值越大,意味着聚类的效果越好


    三、聚类过程

    有了相似度计算方法和性能度量这两个理论基础,下面就开始对文本分类了。

    1. 分词

    要对中文文本做聚类分析,首先要对文本做分词处理,例如“联想移动通信科技有限公司”,我们希望将其切分为“联想 移动 通信 科技 有限 公司”。python提供专门的中文切词工具“jieba”,它可以将中文长文本划分为若干个单词。

    为了提高分类的准确率,还要考虑两个干扰因素:一是英文字母大小写的影响,为此我们将英文字母统一转换为大写;二是例如 “有限”、“责任”、“股份”、“公司”等通用的词汇,我们将这样的词汇连同“()”、“-”、“/”、“&”等符号作为停用词,将其从分词结果中去除掉,最后得到有效的词汇组合,代码和结果如下:

    # 加载停用词,这里主要是排除“有限公司”一类的通用词
    def loadStopWords(fileName):
        dataMat = []
        fr = open(fileName)
        words = fr.read()
        result = jb.cut(words, cut_all=True)
        newWords = []
        for s in result:
            if s not in newWords:
                newWords.append(s)
        newWords.extend([u'(', u')', '(', ')', '/', '-', '.', '-', '&'])
        return newWords
    
    
    # 把文本分词并去除停用词,返回数组
    def wordsCut(words, stopWordsFile):
        result = jb.cut(words)
        newWords = []
        stopWords = loadStopWords(stopWordsFile)
        for s in result:
            if s not in stopWords:
                newWords.append(s)
        return newWords
    
    
    # 把样本文件做分词处理,并写文件
    def fileCut(fileName, writeFile, stopWordsFile):
        dataMat = []
        fr = open(fileName)
        frW = open(writeFile, 'w')
        for line in fr.readlines():
            curLine = line.strip()
            curLine1 = curLine.upper()  # 把字符串中的英文字母转换成大写
            cutWords = wordsCut(curLine1, stopWordsFile)
            for i in range(len(cutWords)):
                frW.write(cutWords[i])
                frW.write('\t')
            frW.write('\n')
            dataMat.append(cutWords)
        frW.close()

    2. 构建词袋模型

    文本被切分成单词后,需要进一步转换成向量。先将所有文本中的词汇构建成一个词条列表,其中不含重复的词条。然后对每个文本,构建一个向量,向量的维度与词条列表的维度相同,向量的值是词条列表中每个词条在该文本中出现的次数,这种模型叫做词袋模型。例如,“阿尔西集团”和“阿尔西制冷工程技术(北京)有限公司”两个文本切词后的结果是“阿尔西 集团”和“阿尔西 制冷 工程技术 北京”,它们构成的词条列表是[阿尔西, 集团, 制冷, 工程技术, 北京],对应的词袋模型分别是[1,1,0,0,0],[1,0,1,1,1]。

    # 创建不重复的词条列表
    def createVocabList(dataSet):
        vocabSet = set([])
        for document in dataSet:
            vocabSet = vocabSet | set(document)
        return list(vocabSet)
    
    
    # 将文本转化为词袋模型
    def bagOfWords2Vec(vocabList, inputSet):
        returnVec = [0] * len(vocabList)
        for word in inputSet:
            if word in vocabList:
                returnVec[vocabList.index(word)] += 1
            else:
                print "the word: %s is not in my Vocabulary!" % word
        return returnVec

    3. 权值转换

    TF-IDF是一种统计方法,用来评估一个词条对于一个文件集中一份文件的重要程度。TF-IDF的主要思想是:如果某个词在一篇文章中出现的频率TF高,并且在其他文件中很少出现,则认为此词条具有很好的类别区分能力,适合用来分类。将词袋向量转换为TF-IDF权值向量,更有利于判断两个文本的相似性。

    • TF(词频,term frequency):

    tf_{i,j} =\frac{n_{i,k}}{\sum_{k}n_{k,j}}

           分子是词条t_i在文件d_j中出现的次数,分母是文件d_j中所有词条出现的次数之和。

    • IDF(逆向文件频率,inverse document frequency):

    idf_i=log\frac{\left | D \right |}{\left |\left \{ j:t_i\in d_j \right \} \right |}

            对数内的分子是文件总数,分母是包含词条t_i的文件数,如果该词不存在,就会导致分母为零,因此一般使用1+\left |\left \{ j:t_i\in d_j \right \} \right |作为分母。

    tfidf_{i,j} = tf_{i,j}\times idf_i

    # 计算所有文本包含的总词数
    def wordsCount(dataSet):
        wordsCnt = 0
        for document in dataSet:
            wordsCnt += len(document)
        return wordsCnt
    
    
    # 计算包含某个词的文本数
    def wordInFileCount(word, cutWordList):
        fileCnt = 0
        for i in cutWordList:
            for j in i:
                if word == j:
                    fileCnt = fileCnt + 1
                else:
                    continue
        return fileCnt
    
    
    # 计算权值,并存储为txt
    def calTFIDF(dataSet, writeFile):
        allWordsCnt = wordsCount(dataSet)  # 所有文本的总词数
        fileCnt = len(dataSet)  # 文本数
        vocabList = createVocabList(dataSet)  # 词条列表
        # tfidfSet = []
        frW = open(writeFile, 'w')
        for line in dataSet:
            wordsBag = bagOfWords2Vec(vocabList, line)  # 每行文本对应的词袋向量
            lineWordsCnt = 0
            for i in range(len(wordsBag)):
                lineWordsCnt += wordsBag[i]  # 计算每个文本中包含的总词数
            tfidfList = [0] * len(vocabList)
            for word in line:
                wordinfileCnt = wordInFileCount(word, dataSet)  # 包含该词的文本数
                wordCnt = wordsBag[vocabList.index(word)]  # 该词在文本中出现的次数
                tf = float(wordCnt) / lineWordsCnt
                idf = math.log(float(fileCnt) / (wordinfileCnt + 1))
                tfidf = tf * idf
                tfidfList[vocabList.index(word)] = tfidf
            frW.write('\t'.join(map(str, tfidfList)))
            frW.write('\n')
            # tfidfSet.append(tfidfList)
    
        frW.close()

    4. 计算余弦相似度

    前面已经介绍过,相对欧氏距离,余弦相似度更适合文本分类,Python实现如下:

    # 计算余弦距离
    def gen_sim(A, B):
        num = float(dot(mat(A), mat(B).T))
        denum = linalg.norm(A) * linalg.norm(B)
        if denum == 0:
            denum = 1
        cosn = num / denum
        sim = 0.5 + 0.5 * cosn  # 余弦值为[-1,1],归一化为[0,1],值越大相似度越大
        sim = 1 - sim  # 将其转化为值越小距离越近
        return sim

    5. 使用K-均值聚类算法分类

    K-均值是将数据集划分为k个簇的算法,簇的个数k是用户给定的,每个簇通过其质心(簇中所有点的中心)来描述。K-均值算法的工作流程是:

    (1)随机确定k个初始点作为质心。

    (2)将数据集中的每个点找到距离最近的质心,并将其分配到该质心对应的簇中。

    (3)将每个簇的质心更新为该簇中所有点的平均值。

    (4)重复第(2)、(3)步骤,直到簇的分配结果不再变化。

    为了评价聚类的质量,定义一种用于衡量聚类效果的指标SSE(Sum of Squared Error,误差平方和),误差是指样本到其质心的距离。SSE值越小,表示数据点越接近质心。

    由于K-均值算法是随机选取质心,因此可能会收敛到局部最小值,而非全局最小值。为了克服这个问题,提出了一种二分K-均值算法。该算法的思路是将所有点作为一个簇,然后将该簇一分为二。之后选择一个能最大程度降低SSE值的簇继续进行划分,直到得到用户指定的簇数目为止。

    注意:该算法需要确定簇的个数,而我的需求中分类的个数是未知的。因此,希望通过观察性能度量指标DI和DBI的变化趋势来确定一个合适k值。

    性能度量指标的实现:

    # 计算簇内两个样本间的最大距离
    def diamM(dataSet):
        maxDist = 0
        m = shape(dataSet)[0]
        if m > 1:
            for i in range(m):
                for j in range(i + 1, m):
                    dist = gen_sim(dataSet[i, :], dataSet[j, :])
                    if dist > maxDist:
                        maxDist = dist
        return maxDist
    
    
    # 计算两个簇间,样本间的最小距离
    def dMin(dataSet1, dataSet2):
        minDist = 1
        m = shape(dataSet1)[0]
        n = shape(dataSet2)[0]
        for i in range(m):
            for j in range(n):
                dist = gen_sim(dataSet1[i, :], dataSet2[j, :])
                if dist < minDist:
                    minDist = dist
        return minDist
    
    
    # 计算簇内样本间的平均距离
    def avg(dataSet):
        m = shape(dataSet)[0]
        dist = 0
        avgDist = 0
        if m > 1:
            for i in range(m):
                for j in range(i + 1, m):
                    dist += gen_sim(dataSet[i, :], dataSet[j, :])
            avgDist = float(2 * dist) / (m * (m - 1))
        return avgDist
    

    二分K-均值算法实现:

    def biKmeans(dataSet, k, distMeas=gen_sim):
        m = shape(dataSet)[0]
        clusterAssment = mat(zeros((m, 2)))
        SSE = []  # 用于记录每次迭代的总误差
        DI = 0  # DI指数,用于衡量簇间的相似度,值越大越好
        DBI = 0
        dmin = 1
        diam = []
        diam.append(diamM(dataSet))
        centroid0 = mean(dataSet, axis=0).tolist()[0]
        centList = [centroid0]  # 创建质心列表,初始只有一个质心
        for j in range(m):  # 计算初始的平方误差
            clusterAssment[j, 1] = distMeas(centroid0, dataSet[j, :]) ** 2
        SSE.append([0, sum(clusterAssment[:, 1]), 1, 0])
        while (len(centList) < k):  # 聚类数小于k时
            lowestSSE = inf
            for i in range(len(centList)):  # 对每个质心循环
                # 获取第i个质心对应的数据集(簇)
                ptsInCurrCluster = dataSet[nonzero(clusterAssment[:, 0].A == i)[0],
                                   :]
                # 对该簇使用k均值算法,分为2个簇
                centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)
                # 计算该簇的总误差
                sseSplit = sum(splitClustAss[:, 1])
                # 计算未分割的其他簇的总误差
                sseNotSplit = sum(
                    clusterAssment[nonzero(clusterAssment[:, 0].A != i)[0], 1])
                # print "sseSplit, and notSplit: ",sseSplit,sseNotSplit
                if (sseSplit + sseNotSplit) < lowestSSE:  # 寻找最小误差对应的簇
                    bestCentToSplit = i
                    bestNewCents = centroidMat
                    bestClustAss = splitClustAss.copy()
                    lowestSSE = sseSplit + sseNotSplit
    
            # 更新簇的分配结果,将多分出来的簇作为最后一个簇
            bestClustAss[nonzero(bestClustAss[:, 0].A == 1)[0], 0] = len(
                centList)  # change 1 to 3,4, or whatever
            bestClustAss[nonzero(bestClustAss[:, 0].A == 0)[0], 0] = bestCentToSplit
            # 更新质心列表
            centList[bestCentToSplit] = bestNewCents[0, :].tolist()[0]
            centList.append(bestNewCents[1, :].tolist()[0])
            # 更新分类结果
            clusterAssment[nonzero(clusterAssment[:, 0].A == bestCentToSplit)[0],
            :] = bestClustAss
            index = len(centList);
            error = sum(clusterAssment[:, 1])
    
            # 新划分的两个簇
            newDataSet1 = dataSet[
                          nonzero(clusterAssment[:, 0].A == bestCentToSplit)[0], :]
            newDataSet2 = dataSet[
                          nonzero(clusterAssment[:, 0].A == len(centList) - 1)[0],
                          :]
    
            # 计算DI指数,该值越大越好
            diam1 = diamM(newDataSet1)
            diam2 = diamM(newDataSet2)
            diam[bestCentToSplit] = diam1
            diam.append(diam2)
    
            for l in range(len(centList) - 1):
                dataSetl = dataSet[nonzero(clusterAssment[:, 0].A == l)[0], :]
                dist = dMin(dataSetl, newDataSet2)
                if dist < dmin:
                    dmin = dist
    
            DI = float(dmin) / max(diam)
    
            # 计算DBI指数,该值越小越好
            maxDBI = 0
            sumDBI = 0
            DBI = 0
            for i in range(len(centList)):
                for j in range(i + 1, len(centList)):
                    dataSeti = dataSet[nonzero(clusterAssment[:, 0].A == i)[0], :]
                    dataSetj = dataSet[nonzero(clusterAssment[:, 0].A == j)[0], :]
                    DBIij = (avg(dataSeti) + avg(dataSetj)) / gen_sim(
                        mat(centList[i]), mat(centList[j]))
                    if DBIij > maxDBI:
                        maxDBI = DBIij
                sumDBI += maxDBI
            DBI = sumDBI / len(centList)
    
            SSE.append([index, error, DI, DBI])
            print '---' + getTime() + '---'
            print u'误差最小的簇是: ', bestCentToSplit
            print u'该簇的长度是: ', len(bestClustAss)
            print u'分为%d个簇,误差是%f' % (index, error)
            print u'分为%d个簇,DI值是%f,DBI值是%f' % (index, DI, DBI)
        return mat(centList), clusterAssment, mat(SSE)

    由于计算DI和DBI值的复杂度较高,先选取500多个样本测试一下效果,得到的趋势如下图所示,然而结果并不理想,DBI值趋于不变,DI值的变化趋势也没有规律。同时,分别对500多个样本划分为200、300、420个簇,经过人工校验,被成功聚类的样本分别为111个、106个、105个。由此可以推断,K-均值算法不适合对厂商名称的分类,分析其原因可能是每个厂商名称所包含的词汇量太少。接下来我们再尝试另一种聚类算法——层次聚类。

    6. 使用层次聚类算法

    层次聚类试图在不同的层次对数据集进行划分,可以采用“自底向上”的聚类策略,也可以采用“自顶向下”的分拆策略。一般采用“自底向上”的策略,它的思路是先将数据集中的每个样本看作一个初始聚类簇,然后找出两个聚类最近的两个簇进行合并,不断重复该步骤,直到达到预设的聚类个数或某种条件。关键是如何计算两个簇之间的距离,每个簇都是一个集合,因此需要计算集合的某种距离即可。例如,给定簇C_iC_j ,可通过以下3种方式计算距离:

    • 最小距离:d_{min}(C_i,C_j)=min_{x\in C_i,z\in C_j}dist(x,z)
    • 最大距离:d_{max}(C_i,C_j)=max_{x\in C_i,z\in C_j}dist(x,z)
    • 平均距离: d_{avg}(C_i,C_j)=\frac{1}{\left | C_i \right |\left | C_j \right |}\sum_{x\in C_i}\sum_{z\in C_j}dist(x,z)

    最小距离由两个簇的最近样本决定,最大距离由两个簇的最远样本决定,平均距离由两个簇的所有样本决定。

    接下来要考虑如何确定一个合适的聚类个数或某种结束条件,具体思路是:

    (1)选定一部分测试样本,对其进行层次聚类分析。

    (2)记算性能度量指标DBI和DI的变化趋势,结合人工校验,得到一个合适的聚类个数和对应的距离阈值。

    (3)将此距离阈值作为聚类结束的条件,对所有样本做聚类分析。此时无需再计算DBI和DI值,计算效率可以大幅提升。

    # 计算两个簇的最小距离
    def distMin(dataSet1, dataSet2):
        minD = 1
        m = shape(dataSet1)[0]
        n = shape(dataSet2)[0]
        for i in range(m):
            for j in range(n):
                dist = gen_sim(dataSet1[i], dataSet2[j])
                if dist < minD:
                    minD = dist
        return minD
    
    
    # 计算两个簇的最大距离
    def distMax(dataSet1, dataSet2):
        maxD = 0
        m = shape(dataSet1)[0]
        n = shape(dataSet2)[0]
        for i in range(m):
            for j in range(n):
                dist = gen_sim(dataSet1[i], dataSet2[j])
                if dist > maxD:
                    maxD = dist
        return maxD
    
    
    # 计算两个簇的评均距离
    def distAvg(dataSet1, dataSet2):
        avgD = 0
        sumD = 0
        m = shape(dataSet1)[0]
        n = shape(dataSet2)[0]
        for i in range(m):
            for j in range(n):
                dist = gen_sim(dataSet1[i], dataSet2[j])
                sumD += dist
        avgD = sumD / (m * n)
        return avgD
    
    
    # 找到距离最近的两个簇
    def findMin(M):
        minDist = inf
        m = shape(M)[0]
        for i in range(m):
            for j in range(m):
                if i != j and M[i, j] < minDist:
                    minDist = M[i, j]
                    minI = i
                    minJ = j
        return minI, minJ, minDist
    
    
    
    # 层次聚类算法
    def hCluster(dataSet, k, dist, distMeas=distAvg):
        m = shape(dataSet)[0]
        clusterAssment = mat(zeros((m, 1)))
        performMeasure = []
        M = mat(zeros((m, m)))  # 距离矩阵
        # 初始化聚类簇,每个样本作为一个类
        for ii in range(m):
            clusterAssment[ii, 0] = ii
    
        for i in range(m):
            for j in range(i + 1, m):
                dataSeti = dataSet[nonzero(clusterAssment[:, 0].A == i)[0], :]
                dataSetj = dataSet[nonzero(clusterAssment[:, 0].A == j)[0], :]
                M[i, j] = distMeas(dataSeti, dataSetj)
                M[j, i] = M[i, j]
            if mod(i,10) == 0: print i
        q = m  # 设置当前聚类个数
        minDist = 0
        # while (q > k):
        while (minDist < dist):
            i, j, minDist = findMin(M)  # 找到距离最小的两个簇
            # 把第j个簇归并到第i个簇
            clusterAssment[nonzero(clusterAssment[:, 0].A == j)[0], 0] = i
            for l in range(j + 1, q):  # 将j之后的簇重新编号
                clusterAssment[nonzero(clusterAssment[:, 0].A == l)[0], 0] = l - 1
            M = delete(M, j, axis=0)
            M = delete(M, j, axis=1)
            for l in range(q - 1):  # 重新计算第i个簇和其他簇直接的距离
                dataSeti = dataSet[nonzero(clusterAssment[:, 0].A == i)[0], :]
                dataSetl = dataSet[nonzero(clusterAssment[:, 0].A == l)[0], :]
                M[i, l] = distMeas(dataSeti, dataSetl)
                M[l, i] = M[i, l]
    
            DBI = DBIvalue(dataSet, clusterAssment, q)
            DI = DIvalue(dataSet, clusterAssment, q)
           
            performMeasure.append([q - 1, minDist, DBI, DI])
    
            q = q - 1
    
            print '---' + getTime() + '---'
            print u'当前簇的个数是:', q
            print u'距离最小的两个簇是第%d个和第%d个,距离是%f,DBI值是%f,DI值是%f' % (
                i, j, minDist, DBI, DI)
    
        return clusterAssment, mat(performMeasure)

    仍然选择K-均值聚类分析的500多个样本,对其进行层次聚类,得到的性能指标变化趋势如下:

    从上图可以看出,DI值呈下降趋势,DBI值呈阶跃上升趋势,根据性能度量的规则(DBI的值越小越好;DI的值越大越好),最优值可能出现阶跃点附近,即划分为471类和445类两个点,同时结合人工校验,可以确定445类更加合理。

    接下来,将k值设置为445进行层次聚类分析,发现仍有少量相似的样本被划分到不同的类。根据业务需求,为了减少后续的核实工作量,我们希望将相似的样本尽可能划分到同一类中,同时可以接受少部分不同的样本划分到同一类,我们给予k值适当的冗余,将其设置为420,再分别基于最大距离、最小距离、平均距离进行分析。

    距离计算方法

    聚到簇中的样本数

    最佳距离

    最大距离

    160

    0.2975

    最小距离

    167

    0.2556

    平均距离

    167

    0.2839

    从以上分类结果看出,采用层次聚类算法对测试样本进行分类,效果明显优于K-均值聚类算法。并且,该算法可以通过学习得到距离阈值作为聚类结束的条件,从而解决了分类个数k值无法确定的问题。

    为了降低个别样本对整体结果的影响,选择基于平均距离的距离分析算法,并将距离阈值设置为0.29,对4574个样本做聚类分析,最后得到3128个类,看一下部分样本的分类效果:


    四、总结

    对文本聚类主要有几个步骤:对文本分词、构建词袋模型、权值转换、计算余弦相似度、选取聚类算法、性能度量、确定聚类结束条件。如果文本所含的词汇量较多,并且已知分类的个数k,可以选择二分K-均值聚类算法。而层次聚类算法根据样本距离来分类,并且可以以样本距离作为分类结束的条件,比较适合k未知的情况。


    代码资源下载地址(留言回复可能不及时,请您自行下载):

    https://download.csdn.net/download/leaf_zizi/12073625

    网盘下载链接: https://pan.baidu.com/s/1EXyaEfQ2K-JVe4mbX41c-Q 提取码: ghas(下载后记得给文章点赞哦)


    参考:

    周志华《机器学习》

    Peter Harrington 《机器学习实战》

    https://blog.csdn.net/songzhilian22/article/details/49636725/

     

    展开全文
  • 分类则取一定阀值的类 - sentence_similarity/目录下以bert为例进行两个句子文本相似度计算,数据格式如data/sim_webank/目录下所示 - predict_bert_text_cnn.py - tet_char_bert_embedding.py - tet_char_bert_...

    68747470733a2f2f696d672e736869656c64732e696f2f707970692f762f4b657261732d54657874436c617373696669636174696f6e68747470733a2f2f7472617669732d63692e636f6d2f796f6e677a68756f2f4b657261732d54657874436c617373696669636174696f6e2e7376673f6272616e63683d6d617374657268747470733a2f2f696d672e736869656c64732e696f2f707970692f646d2f4b657261732d54657874436c617373696669636174696f6e68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f796f6e677a68756f2f4b657261732d54657874436c617373696669636174696f6e3f7374796c653d736f6369616c68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f666f726b732f796f6e677a68756f2f4b657261732d54657874436c617373696669636174696f6e2e7376673f7374796c653d736f6369616c68747470733a2f2f6261646765732e6769747465722e696d2f796f6e677a68756f2f4b657261732d54657874436c617373696669636174696f6e2e737667

    Install(安装)

    pip install Keras-TextClassification

    step2: download and unzip the dir of 'data.rar', 地址: https://pan.baidu.com/s/1I3vydhmFEQ9nuPG2fDou8Q 提取码: rket

    cover the dir of data to anaconda, like '/anaconda/3.5.1/envs/tensorflow13/Lib/site-packages/keras_textclassification/data'

    step3: goto # Train&Usage(调用) and Predict&Usage(调用)

    keras_textclassification(代码主体,未完待续...)

    - Electra-fineture(todo)

    - Albert-fineture

    - Xlnet-fineture

    - Bert-fineture

    - FastText

    - TextCNN

    - charCNN

    - TextRNN

    - TextRCNN

    - TextDCNN

    - TextDPCNN

    - TextVDCNN

    - TextCRNN

    - DeepMoji

    - SelfAttention

    - HAN

    - CapsuleNet

    - Transformer-encode

    - SWEM

    - LEAM

    - TextGCN(todo)

    run(运行, 以FastText为例)

    - 1. 进入keras_textclassification/m01_FastText目录,

    - 2. 训练: 运行 train.py, 例如: python train.py

    - 3. 预测: 运行 predict.py, 例如: python predict.py

    - 说明: 默认不带pre train的random embedding,训练和验证语料只有100条,完整语料移步下面data查看下载

    run(多标签分类/Embedding/test/sample实例)

    - bert,word2vec,random样例在test/目录下, 注意word2vec(char or word), random-word, bert(chinese_L-12_H-768_A-12)未全部加载,需要下载

    - multi_multi_class/目录下以text-cnn为例进行多标签分类实例,转化为multi-onehot标签类别,分类则取一定阀值的类

    - sentence_similarity/目录下以bert为例进行两个句子文本相似度计算,数据格式如data/sim_webank/目录下所示

    - predict_bert_text_cnn.py

    - tet_char_bert_embedding.py

    - tet_char_bert_embedding.py

    - tet_char_xlnet_embedding.py

    - tet_char_random_embedding.py

    - tet_char_word2vec_embedding.py

    - tet_word_random_embedding.py

    - tet_word_word2vec_embedding.py

    keras_textclassification/data

    - 数据下载

    ** github项目中只是上传部分数据,需要的前往链接: https://pan.baidu.com/s/1I3vydhmFEQ9nuPG2fDou8Q 提取码: rket

    - baidu_qa_2019(百度qa问答语料,只取title作为分类样本,17个类,有一个是空'',已经压缩上传)

    - baike_qa_train.csv

    - baike_qa_valid.csv

    - byte_multi_news(今日头条2018新闻标题多标签语料,1070个标签,fate233爬取, 地址为: [byte_multi_news](https://github.com/fate233/toutiao-multilevel-text-classfication-dataset))

    -labels.csv

    -train.csv

    -valid.csv

    - embeddings

    - chinese_L-12_H-768_A-12/(取谷歌预训练好点的模型,已经压缩上传,

    keras-bert还可以加载百度版ernie(需转换,[https://github.com/ArthurRizar/tensorflow_ernie](https://github.com/ArthurRizar/tensorflow_ernie)),

    哈工大版bert-wwm(tf框架,[https://github.com/ymcui/Chinese-BERT-wwm](https://github.com/ymcui/Chinese-BERT-wwm))

    - albert_base_zh/(brightmart训练的albert, 地址为https://github.com/brightmart/albert_zh)

    - chinese_xlnet_mid_L-24_H-768_A-12/(哈工大预训练的中文xlnet模型[https://github.com/ymcui/Chinese-PreTrained-XLNet],24层)

    - term_char.txt(已经上传, 项目中已全, wiki字典, 还可以用新华字典什么的)

    - term_word.txt(未上传, 项目中只有部分, 可参考词向量的)

    - w2v_model_merge_short.vec(未上传, 项目中只有部分, 词向量, 可以用自己的)

    - w2v_model_wiki_char.vec(已上传百度网盘, 项目中只有部分, 自己训练的维基百科字向量, 可以用自己的)

    - model

    - fast_text/预训练模型存放地址

    项目说明

    构建了base基类(网络(graph)、向量嵌入(词、字、句子embedding)),后边的具体模型继承它们,代码简单

    keras_layers存放一些常用的layer, conf存放项目数据、模型的地址, data存放数据和语料, data_preprocess为数据预处理模块,

    模型与论文paper题与地址

    参考/感谢

    训练简单调用:

    from keras_textclassification import train

    train(graph='TextCNN', # 必填, 算法名, 可选"ALBERT","BERT","XLNET","FASTTEXT","TEXTCNN","CHARCNN",

    # "TEXTRNN","RCNN","DCNN","DPCNN","VDCNN","CRNN","DEEPMOJI",

    # "SELFATTENTION", "HAN","CAPSULE","TRANSFORMER"

    label=17, # 必填, 类别数, 训练集和测试集合必须一样

    path_train_data=None, # 必填, 训练数据文件, csv格式, 必须含'label,ques'头文件, 详见keras_textclassification/data

    path_dev_data=None, # 必填, 测试数据文件, csv格式, 必须含'label,ques'头文件, 详见keras_textclassification/data

    rate=1, # 可填, 训练数据选取比例

    hyper_parameters=None) # 可填, json格式, 超参数, 默认embedding为'char','random'

    *希望对你有所帮助!

    展开全文
  • 如何用机器学习对文本分类

    千次阅读 2017-05-30 20:21:58
    需求使用监督学习历史数据训练生成模型,用于预测文本的类别。样本清洗主要将重复的数据删除掉,将错误无效的数据纠正或删除,并检查数据的一致性等。比如我认为长度小于少于13的数据是无效的遂将之删掉。def ...

    需求

    使用监督学习对历史数据训练生成模型,用于预测文本的类别。

    样本清洗

    主要将重复的数据删除掉,将错误无效的数据纠正或删除,并检查数据的一致性等。比如我认为长度小于少于13的数据是无效的遂将之删掉。

    def writeFile(text):
       file_object = open('result.txt','w')
       file_object.write(text)
       file_object.close()
    
    def clear():
       text = ""
       file_obj = open("deal.txt")
       list_of_lines = file_obj.readlines()
       for line in list_of_lines:
         if(len(line)>13):
           text += line
       writeFile(text)
       file_obj.close()

    定好类别集合

    按照样本集人工做好分类,比如分为以下几类:

    编号 类别
    1 环保
    2 交通
    3 手机
    4 法律
    5 汽车

    分类词库

    特征提取涉及到文本分词,由搜狗http://pinyin.sogou.com/dict/可以搜索各种类别的词汇,自己下载下来再整理,它的格式为scel,可以使用深蓝词汇转换工具转成txt方便使用。

    这里写图片描述

    常用算法

    • 朴素贝叶斯
    • Rocchio
    • SVM
    • KNN
    • 决策树
    • 神经网络

    这里选择用SVM,SVM本质上其实也就是一种特殊的两层神经网络,具有高效的学习算法。

    特征集

    使用SVM分类时其中一项重要的工作就是要确定特征集,只有特征集确定好了才能往下计算,那么怎么确定特征集呢?一般的做法可以是将所有样本的词都提取出来作为特征集。比如我们有两个文本
    “小学生上学”和“股票大跌”,那特征集就是{“小学生”,”上学”,”股票”,”大跌”}。

    特征权重

    特征集确定就可以看成是向量的维数,而对于每个样本来说就需要确定每个维度的值了,这个值可以看成是特征的权重,常常用TF-IDF作为值。TF-IDF又是什么?简单来说TF就是某文档中某个term出现的次数,而IDF即逆文档频率,可由下面公式计算:

    IDF=log(Tt)

    其中,T为统计样本中总文档数,t为包含某term的文档数。
    TF和IDF的相乘则为特征权重。

    特征降维

    当统计样本越来越多且每个样本都比较大时,这时可能会导致特征维度特别大。所以可能会要对特征集进行降维处理。特征降维其实就是将一些几乎没影响的维度去掉,以避免维度灾难。有比较多处理方式:比如可以直接定义一个无意义词库将一些没意义的单词去掉、或以词频作为依据选择出代表性的单词、或以其他算法提取出若干热词作为代表性单词、或用经典的卡方校验算法选择代表性单词,以上方式都可以达到降维效果。

    代码

    机器学习库很多,可以选一个自己比较熟悉的且叫有名的库来实现,关键的代码如下:

    double[][] samples = 所有样本特征集及权重数组
    int labelInt[] = 分类标签数组
    SVM<double[]> svm =
            new SVM<double[]>(new LinearKernel(), 1.0, 12, SVM.Multiclass.ONE_VS_ALL);
    svm.learn(samples, labels);
    svm.finish();
    
    double[] test = 测试数据的特征集及权重数组
    svm.predict(x)

    参数

    SVM参数需要选择的主要有两个:核函数和惩罚因子。主要的核函数包括RBF核、线性核、多项式核和Sigmoid核,文本分类中一般可选线性核。惩罚因子用来惩罚分错的样本,惩罚因子越大说明越重视损失,不断增大它最终总能让所有样本都正确分类,但这可能会存在过拟合,影响后面的泛化能力。

    ====广告时间,可直接跳过====

    公众号的菜单已分为“分布式”、“机器学习”、“深度学习”、“NLP”、“Java深度”、“Java并发核心”、“JDK源码”、“Tomcat内核”等,可能有一款适合你的胃口。

    鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有需要的朋友可以购买。感谢各位朋友。

    为什么写《Tomcat内核设计剖析》

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

    欢迎关注:

    这里写图片描述

    展开全文
  • 文本分类

    2017-04-13 17:11:49
    文本分类文献总结1. Recurrent Convolutional Neural Networks for Text Classification将rnn和cnn 结合,来表示文本特征。由于使用cnn需要调节filter_size参数,使cnn使用起来相当繁琐,且cnn与较序列的表示...
  • 文本分类

    千次阅读 2019-07-30 09:29:05
    采用文本分类技术海量数据进行科学地组织和管理显得尤为重要。 文本作为分布最广、数据量最大的信息载体,如何这些数据进行有效地组织和管理是亟待解决的难题。 文本分类是自然语言处理任务中的一项基础性工作,...
  • 文本分类系统

    2014-06-17 15:39:34
    TextClassify文本分类系统适用于中文,英文文本分类。 包括各个文本的关键词输出,可以控制关键词输出个数,也可以关键词加入黑名单和白名单。 关于TextClassify文本分类系统的改进: 改进jieba中文分词词典 ...
  • 词嵌入将不定的文本转换到定的空间内,是文本分类的第一步。 1.1 one-hot 这里的One-hot与数据挖掘任务中的操作是一致的,即将每一个单词使用一个离散的向量表示。具体将每个字/词编码一个索引,然后根据索引...
  • python 基于LDA长文本主题提取并预测分类 Lda算法原理,知乎上有很完善的算法介绍,这里就不多废话了。 数据准备 这一阶段主要是你的问题本进行处理,清洗你的数据。中文文本预处理,主要包括去空格、去表单符号、...
  • 使用sklearn的机器学习模型完成文本分类 常见概念 机器学习模型 机器学习是能通过经验自动改进的计算机算法的研究。机器学习通过历史数据训练出模型对应于人类经验进行归纳的过程,机器学习利用模型新数据...
  • CNN文本分类

    千次阅读 2018-10-18 15:07:18
    做法基本上目前较为浅层的CNN文本分类的做法都是如下图: 将词向量堆积成为二维的矩阵,通过CNN的卷积单元矩阵进行卷积处理,同时使用pooling(通常是1max-pooling)操作,将不等的卷积结果变为等不同的...
  • 这些平台不仅能够处理大数据,使组织能够非结构化数据(如文本分类)进行大规模分析。但在机器学习方面,大数据系统和机器学习工具之间仍然存在差距。流行的机器学习python库,如scikit-learn和Gensim,经过高度优化...
  • 作者|GUEST编译|VK来源|Analytics Vidhya概述在AWS电子病历上建立John Snow实验室的Spark NLP,并使用该库BBC文章进行简单的文本分类。介绍自然语言处理是全球数据科学团队的重要过程之一。随着数据的不断增长,...
  • 基于内容的小说文本分类Chat 简介:本场 Chat 将分享一种小说长文本进行自动分类的算法,此种文本分类思路不涉及很复杂的机器学习算法,也不需要针对大量的小说数据训练...
  • TextRNN和TextRCNN实现文本分类

    千次阅读 2019-03-18 00:17:57
    这里的文本可以一个句子,文档(短文本,若干句子)或篇章(长文本),因此每段文本的长度都不尽相同。在文本进行分类时,我们一般会指定一个固定的输入序列/文本长度:该长度可以是最长文本/序列的长度,此时其他所有...
  • cnn-文本分类

    2019-03-21 13:01:00
    一张图解释CNN如何进行文本分类。 照这个思路,cnn可以解决很多时序问题,与rnn相比,只是不能解决一多、多多的问题。 中文大概这样 参考资料: ...
  • 在AWS电子病历上建立John Snow实验室的Spark NLP,并使用该库BBC文章进行简单的文本分类。 介绍 自然语言处理是全球数据科学团队的重要过程之一。随着数据的不断增长,大多数组织已经转移到大数据平台,如...
  • 分别在长文本语料集和两个短文本语料集上,抽取具有依存关系的词,并利用这些词作为特征进行分类实验。实验结果表明:依存关系能够作为有效的特征进行文本分类,并能够改善文本分类的性能;单独把依存关系作为特征,不...
  • NLP实战之HAN文本分类

    2020-05-20 17:07:33
    HAN(层叠注意力)神经网络文本分类 ...这篇论文表示,文档/较长文本进行分类的时候,仅仅word粒度进行Attention是不够的,还需要各个句子(短句)进行Attention的学习,不同句子也需要分配不同的权重,每
  • Task3 基于机器学习的文本分类 学习目标 学会TF-IDF的原理和使用 使用sklearn的机器学习模型完成文本分类 文本表示方法 文本表示成计算机能够运算的数字或向量的方法一般称为词嵌入(Word Embedding)方法:将不定...
  • HAN文本分类与tensorflow实现

    千次阅读 2019-02-15 16:47:49
     前面介绍了LSTM_CNN、RCNN等文本分类方法,这几种方法都是将整个文本进行encoder,但是我们知道,RNN对于句子或文档其实编码能力是有限的,会丢失掉句子中的信息,因此,为了克服这个问题,Nikolaos等人在2017...
  • TextRNN用于文本分类

    千次阅读 2019-03-10 14:32:29
    1.单向RNN结构 上述公式中,权重矩阵U、V、W共享 ...普通的RNN网络中只有S_t = f(Ux_t+WS_t-1),这种结构无法捕捉到长文本中远距离相关的特征,同时隐藏层的状态短期的输入非常敏感,也会产生梯度爆炸或梯...
  • NLP对抗文本攻击分类

    2020-11-23 15:03:42
    缺点:长文本计算THS和TTS耗时长;随机扰动导致可读性差 基于迁移性攻击:该攻击方法首先基于与目标攻击模型的训练数据同分布的数据训练一个源文本分类模型。然后,利用 HotFlip白盒攻击方法针对源文本分类模型生成...
  • 这里的文本可以一个句子,文档(短文本,若干句子)或篇章(长文本),因此每段文本的长度都不尽相同。在文本进行分类时,我们一般会指定一个固定的输入序列/文本长度:该长度可以是最长文本/序列的长度,此时其他所有...
  • 使用LSTM Bi-GRU对文本评论进行分类 消费者根据较早购买相应产品的同行客户提供的评论从网上购买产品。 人们很难阅读整个评论,然后做出特定决定,在这种情况下,总结评论客户非常有用。 为了解决此问题,可以使用...
  • 数据其实也没啥好说的,就是由段落和表征这个段落情感的标志所构成的一个个,用pandas读进来这个样子: id sentiment review 5814_8 1 With all this stuff going down at the moment w… 381_9 1 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 581
精华内容 232
关键字:

对长文本分类