前阵子分享了 实验 | 使用 Stanford Glove 代码训练中文语料的 Glove 模型 ,后来我偷偷的修改了这篇技术文, 将 C 代码封装到 cntext2.x 内, 原来训练代码行几十行, 现在只需要两行就可以训练 GloVe 模型。

import cntext as ct
g_wv = ct.GloVe(corpus_file='语料文件.txt', window_size=15, vector_size=200, lang='chinese')



一、检查数据

裁判文书数据集,每个月份存储到一个 csv, 每个年份有一个对应的文件夹。下图是 2021 年的文件夹截图


csv 字段格式是一致的,我们只需要找一个文件,尝试着读取前 5 行,查看数据中有哪些字段。

import pandas as pd

df = pd.read_csv('2013/2013-01.csv', nrows=5)
df.dropna(subset=['文书内容'], inplace=True)
df.head(1)



二、训练词向量

2.1 构造语料

我们只从 csv 中选取 “文书内容” ,并将其存储到语料 txt 文件中。但全部裁判文书数据量高达 300G, 我希望文本语料控制在 10G 左右。

2010/2011/2013 这三个年度的数据只有几百 M, 数据全部保留。 剩下的年份,设置不同的抽样比例,尽可能将每年生成的语料 txt 文件控制在 1G 左右。

年份 解压后文件大小 抽样比例 语料 txt 大小
2010 761M 100% 684M
2011 452M 100% 396M
2012 757M 100% 665M
2013 5.13G 20% 984M
2014 23.7G 4% 905M
2015 33.6G 3% 968M
2016 39.9G 2.4% 914M
2017 44.6G 2.2% 882M
2018 24.8G 4% 875M
2019 48.3G 2% 833M
2020 91.2G 1% 779M
2021 32.3G 3% 816M
import os
import pandas as pd
from tqdm import tqdm

# 年份、抽样比例
year_fracs = [
    ('2010', 1), ('2011', 1), ('2012', 1),
    ('2013', 0.2), ('2014', 0.04), ('2015', 0.03),
    ('2016', 0.024), ('2017', 0.022), ('2018', 0.04),
    ('2019', 0.02), ('2020', 0.01), ('2021', 0.03)
    ]



with open('裁判文书.txt', 'w', encoding='utf-8') as corpus_file:
    for year, frac in tqdm(year_fracs):
        csvfs = [f'{year}/{csvf}' for csvf in os.listdir(year) if '.csv' in csvf]
        for csvf in csvfs:
            # 为节省内存开销,
            # 只读 csv 中的 “文书内容” 一个字段,
            # 且设置 chunksize 分批次读取
            chunk_dfs = pd.read_csv(csvf, usecols=['文书内容'], chunksize=10000)
            for chunk_df in chunk_dfs:
                chunk_df.dropna(subset=['文书内容'], inplace=True)
                text = '\n'.join(chunk_df['文书内容'].values)
                corpus_file.write(text)

2.2 训练 GloVe

使用 cntext2.x 对代码进行了优化, 几个 G 的语料在 cntext 内预处理时候不会一次性读取全部内容,所以一般情况不会出现内存溢出问题。

基于语料 裁判文书.txt 训练 GloVe 词嵌入语言模型 ,参数 window_size=15, vector_size=200, 结果会自动保存到 output 文件夹内。

使用 cntext2.1.6, 代码如下

import cntext as ct

g_wv = ct.GloVe(corpus_file='裁判文书.txt', window_size=15, vector_size=200, lang='chinese')

Run

Mac(Linux) System, Enable Parallel Processing
Cache output/裁判文书_cache.txt Not Found, Preprocessing Corpus
Processing Corpus: 100%|██████████████████| 2502938/2502938 [26:37<00:00, 1566.54it/s]
Reading Preprocessed Corpus from output/裁判文书_cache.txt
Start Training GloVe
GloVe Training Cost 1223s.
Output Saved To: output/裁判文书-Word2Vec.200.15.bin

训练总耗时 1223s, 约 20 分钟。模型保存在 output/裁判文书-Word2Vec.200.15.bin, 该模型文件大小约为 1.58G。


2.3 评估模型

使用近义法和类比法, 判断模型的表现。详情可查看文档

ct.evaluate_similarity(g_wv)

Run

近义测试: similarity.txt
/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/cntext/model/evaluate_data/similarity.txt
Processing Similarity Test: 100%|███████████| 537/537 [00:00<00:00, 131978.28it/s]

评估结果:
+----------+------------+----------------------------+
| 发现词语 | 未发现词语 | Spearman's Rank Coeficient |
+----------+------------+----------------------------+
|   432    |    105     |            0.37            |
+----------+------------+----------------------------+

近义测试: Spearman’s Rank Coeficient 系数取值[-1, 1], 取值越大, 说明模型表现越好。

ct.evaluate_analogy(g_wv)

Run

类比测试: analogy.txt
/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/cntext/model/evaluate_data/analogy.txt
Processing Analogy Test: 100%|████████████████| 1198/1198 [00:48<00:00, 24.75it/s]

评估结果:
+--------------------+----------+------------+------------+----------+
|      Category      | 发现词语 | 未发现词语 | 准确率 (%) | 平均排名 |
+--------------------+----------+------------+------------+----------+
| CapitalOfCountries |   507    |    170     |    7.69    |   4.38   |
|   CityInProvince   |   175    |     0      |   98.86    |   1.39   |
| FamilyRelationship |   272    |     0      |   73.53    |   1.56   |
|   SocialScience    |    8     |     62     |   25.00    |   7.00   |
+--------------------+----------+------------+------------+----------+

类比测试:

  • CapitalOfCountries 裁判文书语料在此项表现较差, 应该是数据库中涉外的案件较少。
  • CityInProvince 裁判文书语料在此项表现如此优异,是因为几乎全为国内案件, 而案件描述一般会交待案发的省市等信息。
  • FamilyRelationship 裁判文书语料中表现较好, 可能很多的案件会描述案件相关社会关系。
  • SocialScience 裁判文书语料在此项表现一般, 应该是语料中常见的社会科学词语提及较少。

整体而言,语料训练的效果很不错,抓住了数据场景的独特性语义。



三、使用 GloVe

3.1 导入模型

使用 cntext2.1.6 读取很简单, 代码如下

g_wv = ct.load_w2v('output/裁判文书-Word2Vec.200.15.bin')
print(type(g_wv))
print(f'模型词汇量: {len(g_wv.wv)}')

Run

<class 'gensim.models.keyedvectors.KeyedVectors'>
模型词汇量: 2099102

3.2 KeyedVectors 的操作方法(或属性)

方法 描述
KeyedVectors.index_to_key 获取词汇表中的所有单词。
KeyedVectors.key_to_index 获取单词到索引的映射。
KeyedVectors.vector_size 获取 GloVe 模型中任意词向量的维度。
KeyedVectors.get_vector(word) 获取给定单词的词向量。
KeyedVectors.similar_by_word(word, topn=10) 获取某词语最相似的 10 个近义词。
KeyedVectors.similar_by_vector(vector, topn=10) 获取词向量最相似的 10 个近义词。

3.3 词表

查看词表

g_wv.index_to_key

Run

['被告',
 '原告',
 '本院',
 '公司',
 '规定',
 '执行',
 ...
]


查看词汇映射表

g_wv.key_to_index

Run

{'被告': 0,
 '原告': 1,
 '本院': 2,
 '公司': 3,
 '规定': 4,
 '执行': 5,
 ...
}

3.4 查看词向量

# 查询某词的词向量
g_wv.get_vector('经济')

Run

array([ 2.909250e-01,  9.074450e-01,  5.231860e-01,  5.381490e-01,
       -2.813620e-01,  2.661690e-01,  1.045510e-01, -4.516240e-01,
       -2.186710e-01,  1.867590e-01, -4.870700e-01, -1.803480e-01,
       -6.361140e-01, -8.739630e-01,  3.418450e-01,  7.470900e-02,
        ......
        ......
        2.636230e-01, -2.538920e-01, -2.442900e-02,  5.847510e-01,
        5.135750e-01, -4.009650e-01, -3.629850e-01,  2.332400e-01,
       -3.069630e-01, -4.182810e-01,  3.937240e-01, -8.510000e-01,
        7.894350e-01,  3.969710e-01,  7.895660e-01,  4.881190e-01],
      dtype=float32)

# 查询多个词的词向量
g_wv.get_mean_vector(['经济', '犯罪'])

Ruj

array([ 0.02923387,  0.04620265,  0.03790346,  0.01160904, -0.02162073,
        0.01537724,  0.02025648, -0.03336571, -0.00447518, -0.00529976,
       -0.02856204,  0.01545951,  0.00780857, -0.05398807,  0.02195465,
        0.03140446, -0.02007412,  0.08278576, -0.027172  , -0.00272319,
       ......
        0.0291778 ,  0.03382879, -0.00913138,  0.04487584,  0.06375133,
        0.032144  , -0.02788475,  0.05068161,  0.0122064 ,  0.01759091,
       -0.05560436,  0.00272704, -0.01176615, -0.08875326,  0.00767812,
       -0.00486504,  0.10119167, -0.01212235,  0.06018812,  0.02998512],
      dtype=float32)

3.5 近义词

根据词语查找最相似的 10 个词

g_wv.similar_by_word('动机', topn=10)

Run

[('主观', 0.6688777804374695),
 ('意图', 0.6248725652694702),
 ('恶性', 0.6005507111549377),
 ('蓄意', 0.5913136005401611),
 ('卑劣', 0.5908187627792358),
 ('作案动机', 0.5703221559524536),
 ('心态', 0.5640602707862854),
 ('故意', 0.5533956289291382),
 ('显而易见', 0.5524264574050903),
 ('恶意', 0.5509642958641052)]

根据某词的词向量查询最相似的 10 个词

g_wv.similar_by_vector(g_wv.get_vector('动机'), topn=10)

Run

[('动机', 0.9999999403953552),
 ('主观', 0.6688777804374695),
 ('意图', 0.6248724460601807),
 ('恶性', 0.600550651550293),
 ('蓄意', 0.5913134813308716),
 ('卑劣', 0.5908187627792358),
 ('作案动机', 0.5703221559524536),
 ('心态', 0.5640602707862854),
 ('故意', 0.5533955693244934),
 ('显而易见', 0.5524263381958008)]

多个词求得均值向量

purpose_vector = g_wv.get_mean_vector(['动机',  '意图', '目的'])
g_wv.similar_by_vector(purpose_vector, topn=20)

Run

[('意图', 0.9032057523727417),
 ('目的', 0.8639562726020813),
 ('动机', 0.8277378678321838),
 ('主观', 0.7455390095710754),
 ('恶意', 0.7291366457939148),
 ('故意', 0.7236210107803345),
 ('客观', 0.7146263122558594),
 ('企图', 0.7049675583839417),
 ('行为', 0.6962229609489441),
 ('掩盖', 0.6917882561683655),
 ('所谓', 0.6809536218643188),
 ('并非', 0.667915403842926),
 ('手段', 0.6663289666175842),
 ('利益', 0.6568542718887329),
 ('这种', 0.6558799743652344),
 ('欺骗', 0.6545097231864929),
 ('违背', 0.6538694500923157),
 ('真相', 0.6527130007743835),
 ('显然', 0.6525647640228271),
 ('实质', 0.6521809101104736)]



四、获取模型

内容创作不易, 本文为付费内容,

- 裁判文书-GloVe.200.15.bin   https://pan.baidu.com/s/1a0Fisvnkl8UaQZrHP7olCQ?pwd=8w49

- 更多词向量模型               https://cntext.readthedocs.io/zh-cn/latest/embeddings.html

- 100元                      cntext-2.1.6-py3-none-any.whl  加微信 372335839, 备注「姓名-学校-专业」