{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "title: 文本向量系列-如何基于笔画+拼音构建字向量\n", "date: 2018-08-18 12:17:55\n", "tags: [python, 文本挖掘]\n", "toc: true\n", "xiongzhang: true\n", "xiongzhang_images: [main.jpg]\n", "\n", "---\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 系列文章\n", "\n", "这个是系列博客, 所有文章链接都列在这里, 并持续更新中。\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "中文的词都是由字构成的, 所以有一些研究人员更关心如何用向量来表示一个字, 比如<基于汉字固有属性的中文字向量方法研究>, 该方法结合中文汉字的构词和拼音属性,将中文汉字映射为一个仅32维的空间向量,最后使用卷积神经网络进行语义提取并进行相似性计算。实验结果表明,与现有的短文本相似性计算方法相比,该方法在算法性能及准确率上均有较大的提高。\n", "\n", "### 基于笔画的字向量模型\n", "\n", "(以下引自论文)\n", "\n", "汉字造字法, 即古人所说的“六书”:象形字、会意字、指事字、形声字、转注、假借, 可分为“四体二用”, 其中四体的含义如下。\n", "\n", "- (1) 象形字是描摹事物的记录方式, 是世界上最早的文字, 也是最形象、演变至今保存最完好的一种汉字字形。它纯粹利用图形来刻画文字的使用, 而这些文字与所代表的含义在形状上很相像。如“休”字, 像是一个人依偎着一棵树。“山”就像一座大山的样子, 在一群山的中间有一座高高的山峰。\n", "- (2) 会意字是指两个或两个以上的独体字根据其意思合成的一个字。\n", "- (3) 指事字是一种抽象的造字方法, 当没有或不方便用具体形象刻画的时候就用一种抽象的符号来表示。\n", "- (4) 形声字是在象形字、会意字、指事字三种形式的基础上形成的。它是两个文或字复合成体, 其中一个文或字表示事物的类别, 而另一个表示事物的读音, 也就是人们通常说的“读音认字认半边”。\n", "\n", "另外, 汉字还可以拆分为偏旁和部首, 很多汉字如果具有同一个偏旁, 可能表示同一个意思, 甚至读音也一样。例如, 很多带“扌”的汉字表示为一个动作, 即提、扛、抢、挑等。不仅如此, 根据汉字的结构, 研究发现任何一个汉字都可以分别由横、竖、撇、捺、折的个数线性组合。例如, \n", "\n", "```\n", "良=2·横+0·竖+1·撇+2·捺+2·折\n", "```\n", "\n", "综合以上信息, 本文根据汉字的组成结构和其拼音结构, 提出一种新的字向量模型, 把汉字完全映射到欧氏空间。其中, 每个汉字由一个长度仅为32位的向量组成。相比于One-hot的向量表示方法, 基于笔画的字向量具有非常低的维度, 详见表2。\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 字笔画库\n", "\n", "为了提取文字的笔画, 我们需要一个字表。 下面是我从网上下载到的一个包含笔画的字表, 这是一个文本文件, 我们需要将文件读取处理, 进行格式整理, 然后写入到一个字典里, 这样方便我们后面提取字的笔画。\n", "\n", "\n", "```\n", "汉字 全拼 五笔 郑码 UNICODE GBK 笔画数 部首 笔顺编号\n", "-----------------------------------------------------------------------------------------\n", "0 1 2 3 4 5 6 7 8\n", "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\n", "-----------------------------------------------------------------------------------------\n", "一 yi ggll A 4E00 D2BB 1 一 1\n", "丁 ding sgh AI 4E01 B6A1 2 一 12\n", "丂 yu gnv AZVV 4E02 8140 2 一 15\n", "七 qi agn HD 4E03 C6DF 2 一 15\n", "丄 shang hgd IAVV 4E04 8141 2 一 21\n", "丅 xia ghk AIVV 4E05 8142 2 一 12\n", "丆 myeon myen GAA 4E06 8143 2 一 13\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 解析笔画\n", "\n", "这个笔画文件有很多列数据, 实际上我们用到的就是第一列和最后一列。而除了第2/3/4/5行之外, 我们可以当它是一个csv文件。下面我们从这个文件中提取一个字典, 从汉字到笔顺编号的映射字典。下面就是一些基本的文本处理的代码。\n", "\n", "注意笔顺编号的12345就对应了横竖撇捺折。" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "字典长度: 20902\n" ] } ], "source": [ "from pathlib import Path\n", "\n", "def load_bihua(fpath: Path):\n", " # 读取文件, 以换行符切分文本, 实际上就是划分行\n", " lines = fpath.read_text(encoding='gbk').split('\\n')\n", " # 从第6行以后才是有用的\n", " lines = lines[6:]\n", " dictionary = {}\n", " for line in lines:\n", " # 以空格切分行\n", " cols = line.split()\n", " # 取第一列和最后一列\n", " if cols:\n", " zi, bishun = cols[0], cols[-1]\n", " dictionary[zi] = bishun\n", " return dictionary\n", " \n", "fpath = Path('data/汉字编码表 gbk unicode.txt')\n", "dictionary = load_bihua(fpath)\n", "print('字典长度:', len(dictionary))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 求笔画向量\n", "\n", "我们先求得向量的前五位, 也就是笔画统计构成的向量。" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "我 的向量是: [2. 1. 2. 1. 1.]\n" ] } ], "source": [ "from collections import Counter\n", "import numpy as np\n", "def bihua_victor(zi):\n", " bishun = dictionary[zi]\n", " cnts = Counter(bishun)\n", " vec = np.zeros((5,))\n", " for c in cnts:\n", " i = int(c)\n", " vec[i-1] = cnts[c]\n", " return vec\n", "\n", "zi = '我'\n", "\n", "print(zi, '的向量是:', bihua_victor(zi))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 求拼音向量\n", "\n", "我们这一步要求字向量的6-32位元素数字构成的向量, 姑且叫他拼音向量, 因为在上一篇文章中(文本向量系列-如何基于拼音构建字向量), 我们使用了模块`pypinyin`进行了汉字转拼音, 但是不太符合今天的需求, 我们需要将声调和字母分离, 还好, 这个模块支持我们这样做。" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['wo3', 'men']" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from pypinyin import pinyin\n", "from pypinyin import Style, lazy_pinyin\n", "\n", "lazy_pinyin('我们', style=Style.TONE3)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 1. 0.\n", " 0. 0. 3.]\n" ] } ], "source": [ "def zimu_vec(zi):\n", " '''把字转换成字母向量'''\n", " pinyin: str\n", " pinyin = lazy_pinyin(zi, style=Style.TONE3)[0]\n", " if pinyin[-1] in '1234':\n", " shengdiao = int(pinyin[-1])\n", " pinyin = pinyin[:-1]\n", " else:\n", " shengdiao = 0 # 有的字没有声调\n", " alps = 'abcdefghijklmnopqrstuvwxyz'\n", " vec = np.zeros((len(alps)+1, ))\n", " cnts = Counter(pinyin)\n", " for c in cnts:\n", " i = alps.index(c)\n", " vec[i] = cnts[c]\n", " vec[-1] = shengdiao\n", " return vec\n", "\n", "print(zimu_vec('我'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 最后, 拼接笔画向量和拼音向量" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[2. 1. 2. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0.\n", " 0. 0. 0. 1. 0. 0. 0. 3.]\n" ] } ], "source": [ "def vector(zi):\n", " bh = bihua_victor(zi)\n", " zm = zimu_vec(zi)\n", " # 拼接向量\n", " return np.concatenate((bh, zm))\n", "print(vector('我'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 总结:\n", "\n", "(摘自论文)\n", "\n", "中文短文本在如今高速发展的互联网应用中变得日趋重要, 如何从海量短文本消息中挖掘出有价值的信息, 已成为当前中文自然语言处理中非常重要且具有挑战性的课题。然而, 采用传统的长文本处理方法进行分析往往得不到很好的效果, 其根本原因在于中文短文本消息的语法及其语义的稀疏性。基于此, 该文提出一种基于汉字笔画属性的中文字向量表示方法, 并结合深度学习对短文本消息进行相似性计算。该方法结合中文汉字的构词和拼音属性, 将中文汉字映射为一个仅32维的空间向量, 最后使用卷积神经网络进行语义提取并进行相似性计算。实验结果表明, 与现有的短文本相似性计算方法相比, 该方法在算法性能及准确率上均有较大的提高。" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4" } }, "nbformat": 4, "nbformat_minor": 2 }