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

2019 年 4 月 16 日 PaperWeekly


作者丨苏剑林

单位丨广州火焰信息科技有限公司

研究方向丨NLP,神经网络

个人主页丨kexue.fm


去年 Data Fountain 曾举办了一个“电力专业领域词汇挖掘”的比赛,该比赛有意思的地方在于它是一个“无监督”的比赛,也就是说它考验的是从大量的语料中无监督挖掘专业词汇的能力。 


大赛主页:

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'作者']\
        and not (u'博士' in w or u'硕士' in w or u'研究生' in w)\
        and not (len(set(w)) == 1 and len(w) > 1)\
        and not (w[0in u'一二三四五六七八九十' and len(w) == 2)\
        and re.findall(u'[^一七厂月二夕气产兰丫田洲户尹尸甲乙日卜几口工旧门目曰石闷匕勺]', w)\
        and not u'进一步' in w:
        return True
    else:
        return False


至此,我们就可以完整地执行这个算法了:


# 种子词,在第一步得到的词表中的前面部分挑一挑即可,不需要特别准
start_words = [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'传输'u'检修'u'模拟'u'高频'u'测量'u'样本'u'高级工程师'u'变换'u'试样'u'试验研究'u'平均值'u'向量'u'特征值'u'导体'u'电晕'u'磁通'u'千伏'u'切换'u'响应'u'效率']

cluster_words = find_words(start_words, min_sim=0.6, alpha=0.35)

result2 = result[cluster_words].sort_values(ascending=False)
idxs = [i for i in result2.index if is_good(i)]

pd.Series([i for i in idxs if len(i) > 2][:10000]).to_csv('result_1_2.csv', encoding='utf-8', header=None, index=None)


最终结果(部分):


变压器
发电机
变电站
过电压
可靠性
控制器
断路器
分布式
输电线路
数学模型
滤波器
电容器
故障诊断
神经网络
直流电压
等离子体
联络线
传感器
汽轮机
晶闸管
电动机
约束条件
数据库
可行性
持续时间
整流器
稳定性
调节器
电磁场

后记感想


本文的算法在榜上的成绩大约是 0.22 左右,封榜时排在 100 名左右,榜首已经是 0.49 了,所以从成绩来看其实没什么值得炫耀的。不过当时听说不少人拿现成的专业词典去做字标注,所以当时就没做下去了。要是真的那样子的话,我觉得就很没意思了。

总之,本文算是提供了一个无监督抽取专业词的实现模版,如果读者觉得有可取之处,大方取之即可;如果觉得一无是处,敬请无视它。




点击以下标题查看作者其他文章: 





#投 稿 通 道#

 让你的论文被更多人看到 



如何才能让更多的优质内容以更短路径到达读者群体,缩短读者寻找优质内容的成本呢? 答案就是:你不认识的人。


总有一些你不认识的人,知道你想知道的东西。PaperWeekly 或许可以成为一座桥梁,促使不同背景、不同方向的学者和学术灵感相互碰撞,迸发出更多的可能性。 


PaperWeekly 鼓励高校实验室或个人,在我们的平台上分享各类优质内容,可以是最新论文解读,也可以是学习心得技术干货。我们的目的只有一个,让知识真正流动起来。


📝 来稿标准:

• 稿件确系个人原创作品,来稿需注明作者个人信息(姓名+学校/工作单位+学历/职位+研究方向) 

• 如果文章并非首发,请在投稿时提醒并附上所有已发布链接 

• PaperWeekly 默认每篇文章都是首发,均会添加“原创”标志


📬 投稿邮箱:

• 投稿邮箱:hr@paperweekly.site 

• 所有文章配图,请单独在附件中发送 

• 请留下即时联系方式(微信或手机),以便我们在编辑发布时和作者沟通




🔍


现在,在「知乎」也能找到我们了

进入知乎首页搜索「PaperWeekly」

点击「关注」订阅我们的专栏吧



关于PaperWeekly


PaperWeekly 是一个推荐、解读、讨论、报道人工智能前沿论文成果的学术平台。如果你研究或从事 AI 领域,欢迎在公众号后台点击「交流群」,小助手将把你带入 PaperWeekly 的交流群里。


▽ 点击 | 阅读原文 | 查看作者博客

登录查看更多
15

相关内容

分散式表示即将语言表示为稠密、低维、连续的向量。 研究者最早发现学习得到词嵌入之间存在类比关系。比如apple−apples ≈ car−cars, man−woman ≈ king – queen 等。这些方法都可以直接在大规模无标注语料上进行训练。词嵌入的质量也非常依赖于上下文窗口大小的选择。通常大的上下文窗口学到的词嵌入更反映主题信息,而小的上下文窗口学到的词嵌入更反映词的功能和上下文语义信息。
【2020新书】实战R语言4,323页pdf
专知会员服务
98+阅读 · 2020年7月1日
商业数据分析,39页ppt
专知会员服务
158+阅读 · 2020年6月2日
斯坦福大学经典《自然语言处理cs224n》2020课件合集
专知会员服务
94+阅读 · 2020年5月25日
【干货书】流畅Python,766页pdf,中英文版
专知会员服务
223+阅读 · 2020年3月22日
【干货书】机器学习Python实战教程,366页pdf
专知会员服务
332+阅读 · 2020年3月17日
电力人工智能发展报告,33页ppt
专知会员服务
120+阅读 · 2019年12月25日
R语言自然语言处理:文本分类
R语言中文社区
7+阅读 · 2019年4月27日
[机器学习] 用KNN识别MNIST手写字符实战
机器学习和数学
4+阅读 · 2018年5月13日
干货|复旦中文文本分类过程(文末附语料库)
全球人工智能
21+阅读 · 2018年4月19日
干货 | 自然语言处理(5)之英文文本挖掘预处理流程
机器学习算法与Python学习
7+阅读 · 2018年4月5日
【干货】--基于Python的文本情感分类
R语言中文社区
5+阅读 · 2018年1月5日
干货 | 自然语言处理(3)之词频-逆文本词频(TF-IDF)详解
机器学习算法与Python学习
3+阅读 · 2017年12月19日
为什么『无监督集成学习』乏人问津?
AI研习社
10+阅读 · 2017年10月24日
Arxiv
12+阅读 · 2019年4月9日
Arxiv
3+阅读 · 2018年12月19日
Large-Scale Study of Curiosity-Driven Learning
Arxiv
8+阅读 · 2018年8月13日
Arxiv
3+阅读 · 2018年5月21日
Arxiv
8+阅读 · 2018年5月15日
Arxiv
3+阅读 · 2018年3月13日
Arxiv
3+阅读 · 2018年2月24日
Arxiv
4+阅读 · 2018年2月13日
VIP会员
相关VIP内容
【2020新书】实战R语言4,323页pdf
专知会员服务
98+阅读 · 2020年7月1日
商业数据分析,39页ppt
专知会员服务
158+阅读 · 2020年6月2日
斯坦福大学经典《自然语言处理cs224n》2020课件合集
专知会员服务
94+阅读 · 2020年5月25日
【干货书】流畅Python,766页pdf,中英文版
专知会员服务
223+阅读 · 2020年3月22日
【干货书】机器学习Python实战教程,366页pdf
专知会员服务
332+阅读 · 2020年3月17日
电力人工智能发展报告,33页ppt
专知会员服务
120+阅读 · 2019年12月25日
相关资讯
R语言自然语言处理:文本分类
R语言中文社区
7+阅读 · 2019年4月27日
[机器学习] 用KNN识别MNIST手写字符实战
机器学习和数学
4+阅读 · 2018年5月13日
干货|复旦中文文本分类过程(文末附语料库)
全球人工智能
21+阅读 · 2018年4月19日
干货 | 自然语言处理(5)之英文文本挖掘预处理流程
机器学习算法与Python学习
7+阅读 · 2018年4月5日
【干货】--基于Python的文本情感分类
R语言中文社区
5+阅读 · 2018年1月5日
干货 | 自然语言处理(3)之词频-逆文本词频(TF-IDF)详解
机器学习算法与Python学习
3+阅读 · 2017年12月19日
为什么『无监督集成学习』乏人问津?
AI研习社
10+阅读 · 2017年10月24日
相关论文
Arxiv
12+阅读 · 2019年4月9日
Arxiv
3+阅读 · 2018年12月19日
Large-Scale Study of Curiosity-Driven Learning
Arxiv
8+阅读 · 2018年8月13日
Arxiv
3+阅读 · 2018年5月21日
Arxiv
8+阅读 · 2018年5月15日
Arxiv
3+阅读 · 2018年3月13日
Arxiv
3+阅读 · 2018年2月24日
Arxiv
4+阅读 · 2018年2月13日
Top
微信扫码咨询专知VIP会员