纠错: 深度学习模型优化时快速收敛

在构建问答系统中,使用CNN模型对问题、正答案和负答案进行特征提取。初始模型在5步内损失为0,精度达到1,导致过拟合。原因在于对每个输入独立初始化权重,构建了三个CNN模型,减弱了模型对负答案的约束。修正方法是共享权重,确保所有输入使用同一CNN模型,从而改善模型表现。
部署运行你感兴趣的模型镜像

最近在做问答系统,用CNN深度学习模型分别对问题、正答案和负答案提取特征,得到各自的特征向量。正答案与问题之间的距离比负答案与问题之间的距离要近,距离用夹角的cos值来表示,目标函数是由此确定的。模型出自《Applying Deep Learning To Answer Selection: A Study And An Open Task》,如图1所示。
这里写图片描述
图 1
用tensorflow架构实现该模型,然而做模型优化时,5步以内模型的loss就为0了,精度达到了1,如图2所示。‘
这里写图片描述
图 2
迷糊了一个星期,才找到问题点。
原来是因为在用CNN做特征提取时,我对三个输入(问题、正答案、负答案)分别做了模型weight初始化,相当于建立了三个CNN分别对问题、正答案、负答案做特征抽取。代码如下,调用了conv函数三遍,初始化了W三次,建立了三个CNN模型。

# define a convolution function 
    def conv(input_data):
        pooled_outputs = []
        for i, filter_size in enumerate(filter_sizes):
                with tf.name_scope("conv-maxpool-%s" % filter_size):
                    # Convolution Layer         
            filter_shape = [filter_size, embedding_size, 1, num_filters]
                W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1), name="W")
                b = tf.Variable(tf.constant(0.1, shape=[num_filters]), name="b")
                conv = tf.nn.conv2d(input_data, W, strides=[1, 1, 1, 1],
                padding="VALID", name="conv")
            h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")    
                pooled = tf.nn.max_pool(
                h,
                        ksize=[1, sequence_length - filter_size + 1, 1, 1],
                        strides=[1, 1, 1, 1],
                        padding='VALID',
                        name="pool")    # shape of pooled is [batch_size,1,1,num_filters]
                pooled_outputs.append(pooled)
        return pooled_outputs
    pooled_outputs1=conv(self.embedded_chars1_expanded) # conv-pool outputs
    pooled_outputs2=conv(self.embedded_chars2_expanded)
    pooled_outputs3=conv(self.embedded_chars3_expanded) 

为什么用三个CNN模型做QA系统的答案选择不行呢?此时的模型相当于如下图3的结构。
这里写图片描述
图 3
三个CNN模型分别做特征提取,导致模型对负答案失去约束力,也就是说负答案的卷积神经网络模型使它始终离问题很远(夹角的cos值小),即使此负答案CNN模型的输入是正答案,距离仍然会远(夹角仍然会小)。打个形象的比喻,就像是让负答案乘以0,无论输入怎么变,输出都是零,这样的约束没有了意义。
对代码进行如下修改。

pooled_outputs1 = []
        pooled_outputs2 = []
        pooled_outputs3 = []
        for i, filter_size in enumerate(filter_sizes):
            with tf.name_scope("conv-maxpool-%s" % filter_size):
                filter_shape = [filter_size, embedding_size, 1, num_filters]
                W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1), name="W")
                b = tf.Variable(tf.constant(0.1, shape=[num_filters]), name="b")
                conv = tf.nn.conv2d(self.embedded_chars1_expanded, W, strides=[1, 1, 1, 1],
                                    padding="VALID", name="conv")
                h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")
                pooled = tf.nn.max_pool(
                                h,
                                ksize=[1, sequence_length - filter_size + 1, 1, 1],
                                strides=[1, 1, 1, 1],
                                padding='VALID',
                                name="pool")  # shape of pooled is [batch_size,1,1,num_filters]
                pooled_outputs1.append(pooled)
                conv = tf.nn.conv2d(self.embedded_chars2_expanded, W, strides=[1, 1, 1, 1],
                                    padding="VALID", name="conv")
                h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")
                        pooled = tf.nn.max_pool(
                                h,
                                ksize=[1, sequence_length - filter_size + 1, 1, 1],
                                strides=[1, 1, 1, 1],
                                padding='VALID',
                                name="pool")  # shape of pooled is [batch_size,1,1,num_filters]
                        pooled_outputs2.append(pooled)
                        conv = tf.nn.conv2d(self.embedded_chars3_expanded, W, strides=[1, 1, 1, 1],
                                    padding="VALID", name="conv")
        # print('\n--- shape of cov is {}'.format(conv.get_shape()))
        # Apply nonlinearity
                        h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")
        # Max-pooling over the outputs
                        pooled = tf.nn.max_pool(
                                h,
                                ksize=[1, sequence_length - filter_size + 1, 1, 1],
                                strides=[1, 1, 1, 1],
                                padding='VALID',
                                name="pool")  # shape of pooled is [batch_size,1,1,num_filters]
                        pooled_outputs3.append(pooled)

改过的程序可以正常运行了,准确率和论文描述的相当。

您可能感兴趣的与本文相关的镜像

TensorFlow-v2.15

TensorFlow-v2.15

TensorFlow

TensorFlow 是由Google Brain 团队开发的开源机器学习框架,广泛应用于深度学习研究和生产环境。 它提供了一个灵活的平台,用于构建和训练各种机器学习模型

### 基于深度学习的文字自动纠错模型实现与应用 #### 模型概述 文字自动纠错是一个复杂的自然语言处理任务,旨在识别并纠正文本中的拼写错误、语法错误以及其他类型的输入误差。随着深度学习的发展,这一领域取得了显著进展。现代深度学习模型可以通过大量语料库的学习来捕捉语言模式,进而有效地检测和修正错误。 为了构建高效的文字纠错系统,可以采用序列到序列(Seq2Seq)架构或Transformer结构,这些模型擅长处理变长的输入输出问题,在翻译、对话生成等领域表现优异,同样适用于文字纠错场景[^1]。 #### 数据预处理 在开始训练之前,准备好高质量的数据集至关重要。对于文字纠错任务来说,理想情况下应该拥有成对的真实句子及其对应的正确版本。如果无法获得标注好的数据,则可通过引入人工噪声的方式制造合成样本。具体操作如下: - 收集大规模干净文本资源; - 对原始文本随机注入常见的人为失误,比如字母替换、遗漏字符等; - 将上述两部分组合形成最终训练集合。 此过程有助于增强模型泛化能力,使其能够在面对未曾见过的新情况做出合理判断[^2]。 ```python import pandas as pd from random import choice, randint def add_noise(sentence): """向给定字符串添加模拟打字错误""" noises = ['swap', 'delete', 'insert'] noisy_sentence = list(sentence) idx = randint(0, len(noisy_sentence)-1) action = choice(noises) if action == "swap": try: temp_char = noisy_sentence[idx] noisy_sentence[idx] = noisy_sentence[idx+1] noisy_sentence[idx+1] = temp_char except IndexError: pass elif action == "delete": del noisy_sentence[idx] elif action == "insert": insert_chars = "!@#$%^&*()_+-=[]{}|;:,.<>?/" noisy_sentence.insert(idx, choice(insert_chars)) return ''.join(noisy_sentence) dataframe = pd.DataFrame({'original': ["hello world", "good morning"], 'noised': [add_noise(s) for s in ["hello world", "good morning"]]}) print(dataframe.head()) ``` #### 构建模型 选用合适的框架如TensorFlow/Keras或者PyTorch搭建神经网络。考虑到Seq2Seq模型的特点——它由编码器(encoder)和解码器(decoder)组成;而Transformer则依赖自注意力机制(self-attention),两者都能很好地适应本课题需求。下面给出一段简单的基于PyTorch的Seq2Seq模型定义代码片段: ```python import torch.nn as nn import torch class EncoderRNN(nn.Module): def __init__(self, input_size, hidden_size): super().__init__() self.hidden_size = hidden_size self.embedding = nn.Embedding(input_size, hidden_size) self.gru = nn.GRU(hidden_size, hidden_size) def forward(self, inputs, hidden=None): embedded = self.embedding(inputs).view(1, 1, -1) output, hidden = self.gru(embedded, hidden) return output, hidden class DecoderRNN(nn.Module): def __init__(self, hidden_size, output_size): super().__init__() self.embedding = nn.Embedding(output_size, hidden_size) self.gru = nn.GRU(hidden_size, hidden_size) self.out = nn.Linear(hidden_size, output_size) self.softmax = nn.LogSoftmax(dim=1) def forward(self, inputs, hidden): output = self.embedding(inputs).view(1, 1, -1) output = F.relu(output) output, hidden = self.gru(output, hidden) output = self.softmax(self.out(output[0])) return output, hidden encoder = EncoderRNN(input_lang.n_words, HIDDEN_SIZE).to(device) decoder = DecoderRNN(HIDDEN_SIZE, output_lang.n_words).to(device) ``` #### 训练流程 一旦完成了数据准备以及模型建立之后,就可以着手进行参数更新环节了。一般而言,会经历以下几个阶段: - 初始化权重矩阵; - 定义损失函数(例如交叉熵Loss),并通过反向传播算法计算梯度; - 使用优化器(Adam Optimizer推荐)迭代调整权值直至收敛为止。 值得注意的是,在实际工程实践中还需要考虑诸如过拟合预防措施(Dropout)、早停策略(Early Stopping)等因素以确保最佳性能。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值