锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

实战分享之专业领域词汇无监督挖掘

时间:2023-03-03 17:30:01 1sg30耦合继电器

640


作者苏剑林

广州火焰信息技术有限公司

研究方向丨NLP,神经网络

个人主页丨kexue.fm


去年DataFountain曾经举办过一个电力专业词汇挖掘有趣的是,这场比赛是一场无监督的比赛,也就是说,它测试了从大量语料中挖掘专业词汇的能力。


大赛主页:

https://www.datafountain.cn/competitions/320/details


这确实是界确实是一种有价值的能力,我认为我以前在没有监督的新词发现中做过一些研究,加上没有监督的竞争的新颖性,所以我毫不犹豫地参与了,但最终的排名并不高。


无论如何,分享自己的做法是一种真正意义上的无监督做法,可能对一些读者有一定的参考价值。


基准对比


首先,新词发现部分,用到了我自己写的库 NLP Zero,基本思路是先分别对“比赛所给语料”、“自己爬的一部分百科百科语料”做新词发现,然后两者进行对比,就能找到一批“比赛所给语料”的特征词。 


NLP Zero:

https://kexue.fm/archives/5597


参考的源码是:


from nlp_zero import *
import re
import pandas as pd
import pymongo
import logging
logging.basicConfig(level = logging.INFO, format = '%(asctime)s - %(name)s - %(message)s')


class D: # 读取比赛方所给语料
    def __iter__(self):
        with open('data.txt'as f:
            for l in f:
                l = l.strip().decode('utf-8')
                l = re.sub(u'[^\u4e00-\u9fa5]+'' ', l)
                yield l


class DO: # 读取自己的语料(相当于平行语料)
    def __iter__(self):
        db = pymongo.MongoClient().baike.items
        for i in db.find().limit(300000):
            l = i['content']
            l = re.sub(u'[^\u4e00-\u9fa5]+'' ', l)
            yield l


# 在比赛方语料中做新词发现
f = Word_Finder(min_proba=1e-6, min_pmi=0.5)
f.train(D()) # 统计互信息
f.find(D()) # 构建词库

# 导出词表
words = pd.Series(f.words).sort_values(ascending=False)


# 在自己的语料中做新词发现
fo = Word_Finder(min_proba=1e-6, min_pmi=0.5)
fo.train(DO()) # 统计互信息
fo.find(DO()) # 构建词库

# 导出词表
other_words = pd.Series(fo.words).sort_values(ascending=False)
other_words = other_words / other_words.sum() * words.sum() # 总词频归一化(这样才便于对比)


"""对比两份语料词频,得到特征词。
对比指标是(比赛方语料的词频 + alpha)/(自己语料的词频 + beta);
alpha和beta的计算参考自 http://www.matrix67.com/blog/archives/5044
"""


WORDS = words.copy()
OTHER_WORDS = other_words.copy()

total_zeros = (WORDS + OTHER_WORDS).fillna(0) * 0
words = WORDS + total_zeros
other_words = OTHER_WORDS + total_zeros
total = words + other_words

alpha = words.sum() / total.sum()

result = (words + total.mean() * alpha) / (total + total.mean())
result = result.sort_values(ascending=False)
idxs = [i for i in result.index if len(i) >= 2# 排除掉单字词

# 导出csv格式
pd.Series(idxs[:20000]).to_csv('result_1.csv', encoding='utf-8', header=None, index=None)


语义筛选


注意到,按照上述方法导出来的词表,顶多算是“语料特征词”,但是还不完全是“电力专业领域词汇”。如果着眼于电力词汇,那么需要对词表进行语义上的筛选。

我的做法是:用导出来的词表对比赛语料进行分词,然后训练一个 Word2Vec 模型,根据 Word2Vec 得到的词向量来对词进行聚类。

首先是训练 Word2Vec:


# nlp zero提供了良好的封装,可以直到导出一个分词器,词表是新词发现得到的词表。
tokenizer = f.export_tokenizer()

class DW:
    def __iter__(self):
        for l in D():
            yield tokenizer.tokenize(l, combine_Aa123=False)


from gensim.models import Word2Vec

word_size = 100
word2vec = Word2Vec(DW(), size=word_size, min_count=2, sg=1, negative=10)


然后是聚类,不过这不是严格意义上的聚类,而是根据我们自己跳出来的若干个种子词,然后找到一批相似词来。算法是用相似的传递性(有点类似基于连通性的聚类算法),即 A 和 B 相似,B 和 C也相似,那么 A、B、C 就聚为一类(哪怕A、C从指标上看是不相似的)。

当然,这样传递下去很可能把整个词表都遍历了,所以要逐步加强对相似的限制。比如 A 是种子词,B、C 都不是种子词,A、B 的相似度为 0.6 就定义它为相似,B、C 的相似度要大于 0.7 才能认为它们相似,不然这样一级级地传递下去,后面的词就会离种子词的语义越来越远。

聚类算法如下:


import numpy as np
from multiprocessing.dummy import Queue


def most_similar(word, center_vec=None, neg_vec=None):
    """根据给定词、中心向量和负向量找最相近的词
    """

    vec = word2vec[word] + center_vec - neg_vec
    return word2vec.similar_by_vector(vec, topn=200)


def find_words(start_words, center_words=None, neg_words=None, min_sim=0.6, max_sim=1., alpha=0.25):
    if center_words == None and neg_words == None:
        min_sim = max(min_sim, 0.6)
    center_vec, neg_vec = np.zeros([word_size]), np.zeros([word_size])
    if center_words: # 中心向量是所有种子词向量的平均
        _ = 0
        for w in center_words:
            if w in word2vec.wv.vocab:
                center_vec += word2vec[w]
                _ += 1
        if _ > 0:
            center_vec /= _
    if neg_words: # 负向量是所有负种子词向量的平均(本文没有用到它)
        _ = 0
        for w in neg_words:
            if w in word2vec.wv.vocab:
                neg_vec += word2vec[w]
                _ += 1
        if _ > 0:
            neg_vec /= _
    queue_count = 1
    task_count = 0
    cluster = []
    queue = Queue() # 建立队列
    for w in start_words:
        queue.put((0, w))
        if w not in cluster:
            cluster.append(w)
    while not queue.empty():
        idx, word = queue.get()
        queue_count -= 1
        task_count += 1
        sims = most_similar(word, center_vec, neg_vec)
        min_sim_ = min_sim + (max_sim-min_sim) * (1-np.exp(-alpha*idx))
        if task_count % 10 == 0:
            log = '%s in cluster, %s in queue, %s tasks done, %s min_sim'%(len(cluster), queue_count, task_count, min_sim_)
            print log
        for i,j in sims:
            if j >= min_sim_:
                if i not in cluster and is_good(i): # is_good是人工写的过滤规则
                    queue.put((idx+1, i))
                    if i not in cluster and is_good(i):
                        cluster.append(i)
                    queue_count += 1
    return cluster


规则过滤


总的来说,无监督算法始终是难以做到完美的,在工程上,常见的方法就是人工观察结果然后手写一些规则来处理。在这个任务中,由于前面是纯无监督的,哪怕进行了语义聚类,还是会出来一些非电力专业词汇(比如“麦克斯韦方程”),甚至还保留一些“非词”,所以我写了一通规则来过滤(写得有点丑):


def is_good(w):
    if re.findall(u'[\u4e00-\u9fa5]', w) \
        and len(i) >= 2\
        and not re.findall(u'[较很越增]|[多少大小长短高低好差]', w)\
        and not u'的' in w\
        and not u'了' in w\
        and not u'这' in w\
        and not u'那' in w\
        and not u'到' in w\
        and not w[-1in u'为一人给内中后省市局院上所在有与及厂稿下厅部商者从奖出'\
        and not w[0in u'每各该个被其从与及当为'\
        and not w[-2:] in [u'问题'u'市场'u'邮件'u'合约'u'假设'u'编号'u'预算'u'施加'u'战略'u'状况'u'工作'u'考核'u'评估'u'需求'u'沟通'u'阶段'u'账号'u'意识'u'价值'u'事故'u'竞争'u'交易'u'趋势'u'主任'u'价格'u'门户'u'治区'u'培养'u'职责'u'社会'u'主义'u'办法'u'干部'u'员会'u'商务'u'发展'u'原因'u'情况'u'国家'u'园区'u'伙伴'u'对手'u'目标'u'委员'u'人员'u'如下'u'况下'u'见图'u'全国'u'创新'u'共享'u'资讯'u'队伍'u'农村'u'贡献'u'争力'u'地区'u'客户'u'领域'u'查询'u'应用'u'可以'u'运营'u'成员'u'书记'u'附近'u'结果'u'经理'u'学位'u'经营'u'思想'u'监管'u'能力'u'责任'u'意见'u'精神'u'讲话'u'营销'u'业务'u'总裁'u'见表'u'电力'u'主编'u'作者'u'专辑'u'学报'u'创建'u'支持'u'资助'u'规划'u'计划'u'资金'u'代表'u'部门'u'版社'u'表明'u'证明'u'专家'u'教授'u'教师'u'基金'u'如图'u'位于'u'从事'u'公司'u'企业'u'专业'u'思路'u'集团'u'建设'u'管理'u'水平'u'领导'u'体系'u'政务'u'单位'u'部分'u'董事'u'院士'u'经济'u'意义'u'内部'u'项目'u'建设'u'服务'u'总部'u'管理'u'讨论'u'改进'u'文献']\
        and not w[:2in [u'考虑'u'图中'u'每个'u'出席'u'一个'u'随着'u'不会'u'本次'u'产生'u'查询'u'是否'u'作者']\
    
锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章