栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

python3 extract

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

python3 extract

extract_model.py对文本进行向量化操作的过程解读
    • 由之前extract_vectorize.py保存权重说起
    • 观察train_x到train_y的模型
    • extract_model.py训练完成之后使用指标评估验证集的分数

由之前extract_vectorize.py保存权重说起

之前extract_vectorize.py之中的

np.save(data_extract_npy, embeddings)

是将输出的权重内容保存到对应的文件之中,这里extract_model.py之中首先将先前保存的内容读取出来

data = load_data(data_extract_json)
data_x = np.load(data_extract_npy)
data_y = np.zeros_like(data_x[...,:1])

这里的data是之前抽取的data,还是data[0]为切分出来的字符串,data[1]为对应的下标,data[2]为正式的summaries文本字符串
首先解读这里的data_x[…,:1]的操作
用类似的案例来解读一下这里进行的截取的操作

import tensorflow as tf
import numpy as np
a = np.array([[[1,2,21],[3,4,34]],[[5,6,56],[7,8,78]]])
print('a.shape:',a.shape)
b = a[...,0:2]
print('b :',b)
print('shape.b:',b.shape)

得到的结果如下:

a.shape: (2, 2, 3)
b : [[[1 2]
  [3 4]]
 [[5 6]
  [7 8]]]
shape.b: (2, 2, 2)

可以看出,这里的

b = a[...,0:2]

是截取的最后一维的0开始的两个数值,得到的b的结果
如果操作为

a = np.array([[[1,2,21],[3,4,34]],[[5,6,56],[7,8,78]]])
b = a[...,1]

只取出一个,得到的坐标会减少一维
得到的b的结果为

a.shape = (2,2,3)
b: [[2 4]
     [6 8]]
shape.b: (2,2)

如果想要只取出一个数值并且最后得到的结果保持不变,则写法应该像作者所写的那样,在1的前面加上一个冒号

data_y = np.zeros_like(data_x[...,:1])

然后进入对于data的循环处理之中

for i, d in enumerate(data):
    for j in d[1]:
        j = int(j)
        data_y[i, j] = 1

这里的i,j依次代表第一维,第二维的参数,[i,j]引申起来之后可以不仅仅代表一个对应的数值,也可以代表一整个list

import numpy as np
data = np.array([[2,2],[2,2]])
data[0] = 1

这里得到的结果为

data = 
[[1 1]
 [2 2]]

原因在于在使用data[0] = 1的时候,指向的是[2,2]这整个list,所以整个list都会变成1
同理,这里的data_y[i,j]指向的是第(i,j)个位置的list,数值为[0],这里标记为data_y[i,j] = 1之后[0]变换为[1],这里同样为整个list的变换过程
接下来,为了弄清楚抽取文本这部分是如何学习的,我们需要弄明白前面预测是如何得来的
(具体内容详见上一篇博客文章,这里简单概述一下,就是(256,80)->(256,80,768)->(256,768)
(117,82)->(117,82,768)->(117,768)

最后这无数的向量拼在一起,并且如果第一维如果没有到达最长长度的时候,补充零矩阵,最终的结果为
(20,256,768)
)
而这里的data_y初始化全为零矩阵,如果这一句话被选中为能够作为摘要的备选句子,则将对应的标志置为0,256代表每一个摘要总共有256个句子,这里的train_y = (20,256,1),代表着总共20个摘要,每个摘要256个句子,每个句子1个标签

观察train_x到train_y的模型

其实

data = load_data(data_extract_json)

这一波操作没有什么用,这一个现象我们从训练过程中就能看到,主要是train_x到train_y的操作过程
这里切出来的train_x = (18,256,768)

model.fit(
    train_x,
    train_y,
    epochs=epochs,
    batch_size=batch_size,
    callbacks=[evaluator]
)

对应经历过的网络层结构如下所示:

x = Masking()(x)
x = Dropout(0.1)(x)
#x = (18,256,768)
x = Dense(hidden_size, use_bias=False)(x)
x = Dropout(0.1)(x)
#x = (18,256,384)
x = ResidualGatedConv1D(hidden_size, 3, dilation_rate=1)(x)
x = Dropout(0.1)(x)
#x = (18,256,384)
x = ResidualGatedConv1D(hidden_size, 3, dilation_rate=2)(x)
x = Dropout(0.1)(x)
#x = (18,256,384)
x = ResidualGatedConv1D(hidden_size, 3, dilation_rate=4)(x)
x = Dropout(0.1)(x)
#x = (18,256,384)
x = ResidualGatedConv1D(hidden_size, 3, dilation_rate=8)(x)
x = Dropout(0.1)(x)
#x = (18,256,384)
x = ResidualGatedConv1D(hidden_size, 3, dilation_rate=1)(x)
x = Dropout(0.1)(x)
#x = (18,256,384)
x = ResidualGatedConv1D(hidden_size, 3, dilation_rate=1)(x)
x = Dropout(0.1)(x)
#x = (18,256,384)
x = Dense(1, activation='sigmoid')(x)
#x = (18,256,1)

这里的keras.Masking()网络层的作用,我写了一段代码测试了一下:

from keras.layers import *
import numpy as np
from keras.models import Model
import tensorflow as tf
data = tf.convert_to_tensor([[1,1,1,2,3],[1,1,0,0,0]],dtype=tf.float32)
x_in = Input(shape=(None,5))
x_out = Masking()(x_in)
model = Model(x_in,x_out)
data = model(data)
print('data = ')
print(data)
sess = tf.InteractiveSession()
print(data.eval())

得到的结果

data = 
Tensor("model_1/masking_1/mul:0", shape=(2, 5), dtype=float32)
[[1. 1. 1. 2. 3.]
 [1. 1. 0. 0. 0.]]

???感觉这里没变化???
有大佬知道Masking掩码层的作用,可以评论区指教一下
接下来越过一个Dense的网络层

x = Dense(hidden_size,use_bias=False)(x)
x = Dropout(0.1)(x)

得到x的对应维度(18,256,384)
接下来进入一个作者定义的ResidualGatedConv1D网络层门控卷积的神经网络层之中

class ResidualGatedConv1D(Layer):
    """门控卷积
    """
    def __init__(self, filters, kernel_size, dilation_rate=1, **kwargs):
        super(ResidualGatedConv1D, self).__init__(**kwargs)
        self.filters = filters
        self.kernel_size = kernel_size
        self.dilation_rate = dilation_rate
        self.supports_masking = True

    def build(self, input_shape):
        super(ResidualGatedConv1D, self).build(input_shape)
        self.conv1d = Conv1D(
            filters=self.filters * 2,
            kernel_size=self.kernel_size,
            dilation_rate=self.dilation_rate,
            padding='same',
        )
        self.layernorm = LayerNormalization()

        if self.filters != input_shape[-1]:
            self.dense = Dense(self.filters, use_bias=False)

        self.alpha = self.add_weight(
            name='alpha', shape=[1], initializer='zeros'
        )

    def call(self, inputs, mask=None):
        if mask is not None:
            mask = K.cast(mask, K.floatx())
            inputs = inputs * mask[:, :, None]

        outputs = self.conv1d(inputs)
        gate = K.sigmoid(outputs[..., self.filters:])
        outputs = outputs[..., :self.filters] * gate
        outputs = self.layernorm(outputs)

        if hasattr(self, 'dense'):
            inputs = self.dense(inputs)

        return inputs + self.alpha * outputs

    def compute_output_shape(self, input_shape):
        shape = self.conv1d.compute_output_shape(input_shape)
        return (shape[0], shape[1], shape[2] // 2)

    def get_config(self):
        config = {
            'filters': self.filters,
            'kernel_size': self.kernel_size,
            'dilation_rate': self.dilation_rate
        }
        base_config = super(ResidualGatedConv1D, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

关于门控卷积神经网络层的具体内容回头再作详细的分析,经历了若干个门控卷积层之后,最终进入sigmoid激活函数一下,然后得到最终结果

x = ResidualGatedConv1D(hidden_size, 3, dilation_rate=1)(x)
x = Dropout(0.1)(x)
#x = (18,256,384)
x = Dense(1, activation='sigmoid')(x)
#x = (18,256,1)

这里使用的损失函数为二分交叉熵损失函数,优化器为adam优化器

model = Model(x_in, x)
model.compile(
    loss='binary_crossentropy', optimizer=Adam(), metrics=['accuracy']
)
#抽取式摘要每次只有选与不选,所以是binary_crossentropy
model.summary()
extract_model.py训练完成之后使用指标评估验证集的分数

每一次训练完成之后,使用相应的评测指标

def evaluate(data, data_x, threshold=0.2):
    """验证集评估
    """
    y_pred = model.predict(data_x)[:, :, 0]
    total_metrics = {k: 0.0 for k in metric_keys}
    for d, yp in tqdm(zip(data, y_pred), desc=u'评估中'):
        yp = yp[:len(d[0])]
        yp = np.where(yp > threshold)[0]
        pred_summary = ''.join([d[0][i] for i in yp])
        metrics = compute_metrics(pred_summary, d[2], 'char')
        for k, v in metrics.items():
            total_metrics[k] += v
    return {k: v / len(data) for k, v in total_metrics.items()}


class evaluator(keras.callbacks.Callback):
    """训练回调
    """
    def __init__(self):
        self.best_metric = 0.0

    def on_epoch_end(self, epoch, logs=None):
        metrics = evaluate(valid_data, valid_x, threshold + 0.1)
        #对应指标的限制为0.3
        if metrics['main'] >= self.best_metric:  # 保存最优
            self.best_metric = metrics['main']
            model.save_weights('./weights/extract_model.%s.weights' % fold)
        metrics['best'] = self.best_metric
        print(metrics)

可以看出,这里evaluator后面的部分为老生常谈的每一个epoch之后保存最佳的分数以及对应的权重值,这里重点看每次预测的内容,每次预测的时候放入对应的valid_data(原装的valid_data数组)以及对应的valid_x的输入内容
目前这里的threshold = 0.3
进入到验证集的评估之中

def evaluate(data,data_x,threshold=0.3):
	y_pred = model.predict(data_x)[:,:,0]

这里的data_x = (2,256,1),使用了[:,:,0]之后,得到的结果为(2,256)
接下来初始化总的metric_keys指标分数全为0

total_metrics = {k: 0.0 for k in metric_keys}

然后进入到对于每一个data的计算之中

for d,yp in tqdm(zip(data,y_pred),desc=u'评估中'):
	yp = yp[:len(d[0])]
	yp = np.where(yp > threshold)[0]

首先yp = yp[:len(d[0])]这里将后面填充的0去除掉,只考虑数组中有的数值。
接下来

yp = np.where(yp > threshold)[0]

这里将超出threshold的数值(目前threshold为0.3)的坐标保留下来
接下来将得到的有可能能够匹配的文本取出来并且拼接起来,

pred_summary = ''.join([d[0][i] for i in yp])

得到从原始文本中拼接之后的summary的文本内容,
然后计算拼接得到的文本内容的各项指标

metrics = compute_metrics(pred_summary, d[2], 'char')
for k, v in metrics.items():
    total_metrics[k] += v

最后返回各项指标的平均值

return {k: v / len(data) for k, v in total_metrics.items()}

当计算的平均值较好的时候,保留该模型
注意点:
这里训练抽取模型的时候使用了交叉验证的方法,使得模型训练的效果更好,具体交叉验证切分代码如下:

train_data = data_split(data, fold, num_folds, 'train')
valid_data = data_split(data, fold, num_folds, 'valid')
train_x = data_split(data_x, fold, num_folds, 'train')
valid_x = data_split(data_x, fold, num_folds, 'valid')
train_y = data_split(data_y, fold, num_folds, 'train')
valid_y = data_split(data_y, fold, num_folds, 'valid')
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/341297.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号