之前最末尾的时候,对文本的id有一个打乱顺序的操作
if os.path.exists(data_random_order_json):
idxs = json.load(open(data_random_order_json))
else:
idxs = list(range(len(data)))
np.random.shuffle(idxs)
json.dump(idxs, open(data_random_order_json, 'w'))
data = [data[i] for i in idxs]
with open(data_extract_json, 'w', encoding='utf-8') as f:
for d in data:
f.write(json.dumps(d, ensure_ascii=False) + '\n')
这里的
np.random.shuffle(idxs)
对idxs进行打乱顺序,最后将打乱顺序之后的json内容写入到文件之中去
with open(data_extract_json, 'w', encoding='utf-8') as f:
for d in data:
f.write(json.dumps(d, ensure_ascii=False) + '\n')
当前的程序首先读取对应的json文件内容
data_extract_json = data_json[:-5]+'_extract.json'
data_extract_npy = data_json[:-5]+'_extract'
接着读取出对应的数据(这里读取的是之前打乱顺序的data_random_order_json的数据
data = load_data(data_extract_json)
这里取出对应的data,总共20条数据
接下来对data进行相应的转化
embeddings = convert(data)
这里重点观察convert函数
def convert(data):
"""转换所有样本
"""
embeddings = []
for texts in tqdm(data, desc=u'向量化'):
outputs = predict(texts)
embeddings.append(outputs)
embeddings = sequence_padding(embeddings)
return embeddings
这里的encoder为一个bert的对应网络层,加上一个平均池化层
# 加载bert模型,补充平均池化
encoder = build_transformer_model(
config_path,
checkpoint_path,
)
output = GlobalAveragePooling1D()(encoder.output)
#上面两步为bert+全局池化层
encoder = Model(encoder.inputs, output)
#下面一步重新定义了bert+全局池化层为对应的encoder网络层
接下来将依次预测(没有训练过程),
def convert(data):
"""转换所有样本
"""
embeddings = []
for texts in tqdm(data, desc=u'向量化'):
outputs = predict(texts)
#print('outputs shape = ')
#print(np.array(outputs).shape)
embeddings.append(outputs)
embeddings = sequence_padding(embeddings)
return embeddings
这里的第一波从循环data之中取出的texts为一个字符串数组
---texts = ---
['从龙伟培本人申请的大病求助及其他补助中结付现金,', '不知多少,', '如果钱到位,', '就分落下继承人:', '龙某1、龙某2、龙某3、龙某4。', '”', '该协议有龙某2、龙某1、龙某3、龙某4亲笔签名。', '原告认为,',
............
, '或者人民法院认为审理案件需要的证据,', '人民法院应当调查收集。', '人民法院应当按照法定程序,', '全面地、客观地审查核实证据。']
也就是说这里的texts是从整个text中截取出来的残差不齐的
接下来predict预测之中还有一波代码的解读
for text in texts:
token_ids, segment_ids = tokenizer.encode(text, maxlen=512)
batch_token_ids.append(token_ids)
batch_segment_ids.append(segment_ids)
这里的每一个text为上面
['从龙伟培本人申请的大病求助及其他补助中结付现金,', '不知多少,', '如果钱到位,', '就分落下继承人:', '龙某1、龙某2、龙某3、龙某4。', '”', '该协议有龙某2、龙某1、龙某3、龙某4亲笔签名。', '原告认为,',
............
, '或者人民法院认为审理案件需要的证据,', '人民法院应当调查收集。', '人民法院应当按照法定程序,', '全面地、客观地审查核实证据。']
中对应的文本,然后batch_token_ids和batch_segment_ids依次放入编码之后的内容
batch_token_ids =
[[101, 794, 7987, 836, 1824, 3315, 782, 4509, 6435, 4638, 1920, 4567, 3724, 1221, 1350, 1071, 800, 6133, 1221, 704, 5310, 802, 4385, 7032, 8024, 102], [101, 679, 4761, 1914, 2208, 8024, 102], [101, 1963, 3362, 7178, 1168, 855, 8024, 102]
............
[101, 782, 3696, 3791, 7368, 2418, 2496, 2902, 4212, 3791, 2137, 4923, 2415, 8024, 102], [101, 1059, 7481, 1765, 510, 2145, 6225, 1765, 2144, 3389, 3417, 2141, 6395, 2945, 511, 102]]
以及对应的batch_segment_ids内容
batch_segment_ids =
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
............
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
接下来调用extract_vectorize.py中的sequence_padding内容
batch_token_ids = sequence_padding(batch_token_ids)
batch_segment_ids = sequence_padding(batch_segment_ids)
sequence_padding之后,得到的几组文本的相应的结果
batch_token_ids = (256,80),batch_segment_ids = (256,80)
batch_token_ids = (117,128),batch_segment_ids = (117,128)
batch_token_ids = (191,62),batch_segment_ids = (191,62)
这里前面的256,117,191标记有几组对应的文本,而80,128,62标记这几组文本之中的最大的长度
接下来预测输出
outputs = encoder.predict([batch_token_ids,batch_segment_ids])
得到的outputs的对应结果
outputs = (256,768)
outputs = (117,768)
outputs = (191,768)
这里本质上是先调用bert模型,再对第一维度进行平均池化操作
首先我们查看一下模型的整体流程
# 加载bert模型,补充平均池化
encoder = build_transformer_model(
config_path,
checkpoint_path,
)
#encoder.output = (256,80,768)
output = GlobalAveragePooling1D()(encoder.output)
#output = (256,768)
encoder = Model(encoder.inputs, output)
可以看出越过bert之后的参数为(256,80,768),接下来使用全局池化层对第一个维度进行降维(因为这些句子虽然被用逗号切分开了,但是本质上是一体的),对第一个维度的80进行降维后得到(256,768),本质上是为了后面取出能够得到关键字的索引的值。
全局池化的操作
class GlobalAveragePooling1D(keras.layers.GlobalAveragePooling1D):
"""自定义全局池化
"""
def call(self, inputs, mask=None):
if mask is not None:
mask = K.cast(mask, K.floatx())[:, :, None]
return K.sum(inputs * mask, axis=1) / K.sum(mask, axis=1)
else:
return K.mean(inputs, axis=1)
关键语句
return K.mean(inputs,axis=1)
对第一维进行降维。
维度一致的思想:生出同样维度的第三维,再将第二维进行降维
def convert(data):
"""转换所有样本
"""
embeddings = []
for texts in tqdm(data, desc=u'向量化'):
outputs = predict(texts)
embeddings.append(outputs)
embeddings = sequence_padding(embeddings)
return embeddings
首先这里的embeddings.append(outputs)会压入各种各样不同维度的array的参数,比如[256,768],[117,768],总共有20个数据,所以这里embeddings长度为20
接下来使用sequence_padding将维度对齐第一维最长的那个,得到的embeddings的结果为[20,256,768]
最后将结果存入文件之中
np.save(data_extract_npy,embeddings)
## extract_vectorize.py之中的convert函数调用过程
```python
def convert(data):
"""转换所有样本
"""
embeddings = []
for texts in tqdm(data, desc=u'向量化'):
outputs = predict(texts)
embeddings.append(outputs)
......
这里的outputs分别压入
(256, 768),(117, 768)
......
(239, 768),(117, 768),(129, 768),(150, 768)
的内容,这也就是最后需要sequence_padding进行零填充的原因
embeddings = sequence_padding(embeddings)
填充完之后得到的内容
(20, 256, 768)
最后保存对应输出的文件的内容
np.save(data_extract_npy, embeddings)
print(u'输出路径:%s.npy' % data_extract_npy)