一、摘要
摘要:Bi-SimCut是一种简单但有效的训练策略,以提高神经机器翻译(NMT)的性能,它包括两个过程:双向预训练和单向微调,这两个过程都使用了SimCut, 这是一种简单的正则化方法,强调原始语句和经过Cutoff的语句的输出分布之间的一致性。SimCut并不是一种新的方法,而是Cutoff的简化版本。
二、Token Cutoff介绍、公式
2-1、背景介绍
背景:SimCut并不是一种新的方法,而是Shen等人在论文《A simple but tough to-beat data augmentation approach for natural language understanding and generation》提出的Token Cutoff的简化版本。Shen等人介绍了一套cutoff数据增强方法,并且利用JS散度损失在训练过程中使得原始样本和经过Cutoff之后的样本的输出分布一致。虽然性能可观,但是消耗资源巨大(为其中的四个超参数寻找合适的值耗时并且耗费资源),而Bi-SImCut可以解决这个问题(简单且有效)。
此外:文章还展示了:
- Simcut与预训练语言模型例如mBART的兼容性。
- 结果表明:使用Bi-SimCut的NMT训练在不同量级上的数据都比Transformer取得了显著的改进,并且在几个基准上优于当前的SOTA方法BIBERT。
2-2、Cutoff介绍、架构示意图、公式详解
Cutoff介绍:Shen等人在论文《A simple but tough to-beat data augmentation approach for natural language understanding and generation》提出的Token Cutoff,介绍了一套简单而有效的数据增强策略,建议在一个训练实例中删除部分信息以产生多个扰动样本。为了确保模型完全不能利用已删除输入中的信息,删除过程发生在输入的嵌入层中。
CutOff架构示意图(图示来源于论文):Cutoff示意图,包含
- Token Cutoff(令牌截断):从句子中删除几个单词的情况下,鼓励模型使用其余单词来得到正确的结果。
- feature Cutoff(特征切断):每个输入维度都包含一定的语义信息,某些输入维度被删掉后,模型为了正确预测,就需要给与其余维度封装更加丰富的信息,增加了模型的鲁棒性。
- span cutoff(跨度截断):删除了连续的文本块,鼓励模型利用其余特征来进行预测而不仅仅只是依赖于一小部分显著特征。
(公式详解)
Token Cutoff的交叉熵损失函数为:由三部分组成(原交叉熵损失L c e ( θ ))、使用cutoff的交叉熵损失、kl散度,α和β是平衡他们的标量超参数)
其中:不使用数据增强的交叉熵损失L c e ( θ ) 可以表示为,其中θ是一组模型参数,x,y 代表的是平行语料库,f ( x , y ; θ ) )是一系列的预测概率,ÿ是y的一系列独热码向量。
这里:KL(·|·)表示两个分布的KL散度。有关于KL散度的更多信息请详见附录(有熵到KL散度、JS散度的说明)。L k l ( θ ) 是为了保证原始样本和N个不同的cutoff样本的输出分布的一致性。
三、Bi-SimCut介绍、公式
3-0、Bi-SimCut完整概要
Bi-SimCut:是一种用于提高神经机器翻译(NMT)性能的简单而有效的训练策略。 它包括两个步骤:双向预训练和单向微调。两个步骤都利用了SimCut,这是一种简单的正则化方法,可以强制原始句子和截断句子对应的输出分布之间的一致性。 Bi-SimCut在不通过回译或整合大规模预训练模型的情况下,在五个翻译基准测试中取得了强大的翻译性能。SimCut不是一种新方法,而是针对NMT简化和调整的Cutoff的一个版本,可以被认为是一种扰动-based方法。我们相信,由于SimCut和Bi-SimCut的普适性和简单性,它们可以成为未来NMT研究的强大基线。
为了更好地理解Bi-SimCut,以下是其主要特点:
- 双向预训练和单向微调两个步骤
- 利用SimCut实现正则化,强制输出分布的一致性
- 不需要回译或大规模预训练模型
- 在五个翻译基准测试中取得强大的翻译性能
- 为了更好地理解Bi-SimCut,以下是每个步骤的更详细的说明:
双向预训练:Bi-SimCut使用双向LSTM进行预训练。这个步骤的目的是为了让模型更好地理解输入序列的上下文信息,从而提高翻译性能。在预训练期间,模型首先从左到右处理输入序列,然后再从右到左处理输入序列。每个方向的训练都会在完整的输入序列上进行,而不是在截断的序列上进行。预训练期间使用的损失函数是交叉熵损失函数。
单向微调:在预训练之后,Bi-SimCut使用单向LSTM进行微调,以进一步提高翻译性能。微调期间,模型只从左到右处理输入序列。与预训练期间一样,微调期间也会在完整的输入序列上进行训练,而不是在截断的序列上进行。微调期间使用的损失函数是交叉熵损失函数。
为了更好地理解SimCut的作用,以下是一个例子:
假设有两个句子,一个是原始句子,另一个是对原始句子进行截断后的句子。在使用SimCut时,模型将被要求在这两个句子上产生相同的输出分布。这可以通过将原始句子和截断句子对应的输出分布之间的KL散度最小化来实现。这种正则化方法可以帮助模型避免过度拟合,并提高模型的泛化能力。同时,这种正则化方法有助于防止模型在处理截断句子时出现错误。
总之,Bi-SimCut是一种简单而有效的训练策略,可以提高神经机器翻译的性能。它利用了SimCut这种简单的正则化方法,可以强制原始句子和截断句子对应的输出分布之间的一致性。通过双向预训练和单向微调两个步骤,Bi-SimCut可以在不使用回译或大规模预训练模型的
3-1、Bi-SimCut介绍
Bi-SimCut定义:Bi-SimCut是一种简单但有效的训练策略,以提高神经机器翻译(NMT)的性能,它包括两个过程:双向预训练和单向微调,这两个过程都使用了SimCut, 这是一种简单的正则化方法,强调原始语句和经过Cutoff的语句的输出分布之间的一致性。
提出Bi-SimCut的必要性:尽管Shen等人提出的Token Cutoff令人印象深刻,但是在资源有限的情况下,找到合适的超参数(pcut、α、β、N)是十分耗费时间和资源的,为了减少超参数搜索负担,我们提出了SimCut,一个简单的正则化方法使得原始句子和经过cutoff的样本输出分布保持一致。
使用到的数据集介绍: 使用到的详细数据集如下所示。
- 低资源IWSLT14: en-de
- 标准资源WMT14: en-de
- 高资源WMT17: zh-en
3-2、Bi-SimCut公式
公式:公式是由受虚拟对抗训练(VAT,Sato 等人介绍的一种基于KL的的对抗正则化)启发产生的。对于每个句子对(x,y),我们只生成一个cutoff样本。并且与Token Cutoff论文中使用的策略相同,对于每对句子,SImCut的训练目标为:
Bi-SimCut优点:SimCut中只有两个超参数α和p c u t ,大大简化了Token Cutoff中的超参数搜索步骤。注意:VAT只允许梯度通过KL发散项的右侧反向传播,而梯度在SimCut中被设计为通过KL正则化的的两侧反向传播。
α和p c u t 的影响:α是我们优化问题中控制正则化强度的惩罚参数,p c u t 控制在SimCut中cutoff的百分比。α=3,p c u t=0.05时表现最佳。实验表明,太小或者太大的α和p c u t都不利于模型训练。
总结:L s i m k l ( θ ))保证了原始样本和Cutoff样本的一致性。
3-3、Bi-SimCut训练策略:双向预训练和单向微调
双向预训练和单向微调:首先预训练一个双向NMT模型,并且将其作为初始化来微调一个单向NMT模型,假设我们要训练一个英语-》德语的NMT模型,我们首先将训练句对重构为英语+德语-》德语+英语,其中训练数据集翻倍。然后,我们用新的训练句对训练一个新的双向NMT模型,
四、代码阅读(损失函数部分)
代码解释:
主要用于实现一种名为label_smoothed_cross_entropy_with_simcut的损失函数,它基于Fairseq库,用于训练神经网络模型。这个损失函数结合了标签平滑交叉熵损失(Label Smoothed Cross Entropy)和相似度裁剪损失(SimCut)。
首先,代码导入了所需的库,如math、torch、fairseq等。然后,使用Python的dataclass装饰器定义了一个名为LabelSmoothedCrossEntropyCriterionWithSimCutConfig的数据类,用于存储模型训练过程中的配置参数,包括标签平滑系数(label_smoothing)、正则化项系数(alpha)、相似度裁剪方法中的概率参数(p)、是否报告准确性指标(report_accuracy)等。
接下来,使用fairseq库中的@register_criterion装饰器将LabelSmoothedCrossEntropyCriterionWithSimCutConfig类注册为label_smoothed_cross_entropy_with_simcut。这意味着在训练模型时可以使用label_smoothed_cross_entropy_with_simcut作为损失函数。
LabelSmoothedCrossEntropyWithSimCutCriterion类继承自FairseqCriterion。它的__init__方法接收一系列参数,如任务对象、是否进行句子级别的平均等。该类实现了simcut方法,用于计算相似度裁剪损失。
forward方法用于计算给定样本的损失。它首先计算标签平滑交叉熵损失,然后在训练阶段将其与相似度裁剪损失结合起来。此外,该方法还计算了准确性指标(如果需要)。
compute_loss方法用于计算标签平滑交叉熵损失,compute_accuracy方法用于计算准确性指标。最后,reduce_metrics方法用于汇总分布式训练过程中的日志输出。
总之,这段代码实现了一种名为label_smoothed_cross_entropy_with_simcut的损失函数,用于训练神经网络模型。这个损失函数结合了标签平滑交叉熵损失和相似度裁剪损失,可以在训练过程中使用。
# Copyright (c) Facebook, Inc. and its affiliates. # # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. import math from dataclasses import dataclass, field import torch from fairseq import metrics, utils from fairseq.criterions import FairseqCriterion, register_criterion from fairseq.criterions.label_smoothed_cross_entropy import label_smoothed_nll_loss from fairseq.dataclass import FairseqDataclass from omegaconf import II # 这段代码是用 Python 的 dataclass 装饰器定义了一个名为 LabelSmoothedCrossEntropyCriterionWithSimCutConfig 的数据类,用于存储模型训练过程中的配置参数。 @dataclass class LabelSmoothedCrossEntropyCriterionWithSimCutConfig(FairseqDataclass): label_smoothing: float = field( # 表示平滑系数,为0则表示没有平滑。 default=0.0, metadata={"help": "epsilon for label smoothing, 0 means no label smoothing"}, ) alpha: float = field( # 表示α参数,为0则表示不进行simcut (正则化系数) default=0.0, metadata={"help": "alpha for simcut, 0 means no simcut"}, ) p: float = field( # cutoff的概率参数,为0则表示不进行simcut中的cutoff操作 default=0.0, metadata={"help": "probability for cutoff in simcut, 0 means no cutoff in simcut"}, ) report_accuracy: bool = field( # 表示是否需要报告准确性指标。 default=False, metadata={"help": "report accuracy metric"}, ) ignore_prefix_size: int = field( # 忽略token的数量。 default=0, metadata={"help": "Ignore first N tokens"}, ) # 布尔值,即是否进行句子级别的平均,具体作用是用于计算loss值时,是否对每个句子的loss求平均,如果为True,则对整个batch的所有句子的loss值求平均。 sentence_avg: bool = II("optimization.sentence_avg") # 这段代码是使用fairseq库中的@register_criterion装饰器将LabelSmoothedCrossEntropyCriterionWithSimCutConfig类注册为label_smoothed_cross_entropy_with_simcut # 这意味着在训练模型时可以使用label_smoothed_cross_entropy_with_simcut作为损失函数。 # @register_criterion装饰器提供了一种方便的方式,使用户可以自定义和注册新的损失函数。 @register_criterion( "label_smoothed_cross_entropy_with_simcut", dataclass=LabelSmoothedCrossEntropyCriterionWithSimCutConfig ) # 该类实现了'simcut'方法,用于计算相似度裁剪损失。 class LabelSmoothedCrossEntropyWithSimCutCriterion(FairseqCriterion): def __init__( # 该类接受一下参数 self, # 任务对象 task, # 一个布尔值,表示损失是否以句子为单位进行平均。如果为True,则每个句子的损失将被平均,否则将对所有标记的损失进行平均。 sentence_avg, # 平滑 label_smoothing, # 正则化项系数 alpha=0.0, # 表示simcut方法中的p值 p=0.0, # 一个整数,表示需要忽略的前缀的长度。 ignore_prefix_size=0, # 是否需要报告准确性指标 report_accuracy=False, ): super().__init__(task) self.sentence_avg = sentence_avg self.eps = label_smoothing self.ignore_prefix_size = ignore_prefix_size self.report_accuracy = report_accuracy self.alpha = alpha self.p = 1 - p def simcut(self, model, omega, sample, reduce): """ 这段代码定义了一个名为simcut的方法,接受四个参数:self,model,omega,sample和reduce。 """ # 计算omega矩阵在最后一维的softmax,得到一个概率分布,保存在变量prob中。 prob = torch.nn.functional.softmax(omega, dim=-1) # 判断target序列中是否有padding字符,并生成一个与target同样形状的张量,其中为True的位置代表 # 原来target中非padding字符的位置。 valid_indices = (sample["target"] != self.padding_idx) # 调用模型中的encoder模块对输入进行编码,生成编码器输出。 encoder_out = model.encoder( src_tokens=sample["net_input"]["src_tokens"], src_lengths=sample["net_input"]["src_lengths"], simcut_p=self.p) # 调用模型中的decoder模块对输入进行解码,生成解码器输出。 decoder_out = model.decoder( prev_output_tokens=sample["net_input"]["prev_output_tokens"], encoder_out=encoder_out, simcut_p=self.p) # 使用KL散度来计算解码器输出分布与目标分布之间的差异,并将结果保存在loss变量之中。 loss = torch.nn.functional.kl_div( input=torch.nn.functional.log_softmax(decoder_out[0], dim=-1), target=prob, reduction='none') # 将每个位置的KL散度相加,得到总的散度。 loss = loss.sum(dim=-1) # 将padding位置的kl散度值置为0。 loss = loss * valid_indices.float() # if reduce: loss = loss.sum() return loss def forward(self, model, sample, reduce=True): """Compute the loss for the given sample. 用于计算给定样本的损失。 Returns a tuple with three elements: 1) the loss 2) the sample size, which is used as the denominator for the gradient 3) logging outputs to display while training """ # 调用模型对输入进行前向传播,得到模型的输出 net_output = model(**sample["net_input"]) # 计算模型输出的损失,同时也计算了负对数似然损失,并将结果保存在loss和nll_loss中去。 loss, nll_loss = self.compute_loss(model, net_output, sample, reduce=reduce) # if model.training: # 调用simcut方法,计算基于KL散度的相似度正则项,并将其加到总的损失函数中去。 loss += self.alpha * self.simcut(model, net_output[0], sample, reduce) # 计算样本的大小,用于作为梯度的分母。 sample_size = ( sample["target"].size(0) if self.sentence_avg else sample["ntokens"] ) # 将损失函数的值、负对数似然损失的值、样本中的token数,句子数和样本大小等信息保存在一个字典中。 logging_output = { "loss": loss.data, "nll_loss": nll_loss.data, "ntokens": sample["ntokens"], "nsentences": sample["target"].size(0), "sample_size": sample_size, } # 如果设置了报告准确率的标志,则执行下面的操作。 if self.report_accuracy: # 计算模型的准确率,并将正确预测的数量和总数分别保存在n_correct和total变量中。 n_correct, total = self.compute_accuracy(model, net_output, sample) logging_output["n_correct"] = utils.item(n_correct.data) logging_output["total"] = utils.item(total.data) return loss, sample_size, logging_output def get_lprobs_and_target(self, model, net_output, sample): # 获取归一化的对数概率,net_output代表输出,log_probs代表需要返回对数概率而非原始概率。 lprobs = model.get_normalized_probs(net_output, log_probs=True) # 获取目标序列,其中sample是模型的输入,net_output是输出。 target = model.get_targets(sample, net_output) # 如果ignore_prefix_size 大于0,则会忽略输出序列的前缀部分。 if self.ignore_prefix_size > 0: # lprobs: B x T x C lprobs = lprobs[:, self.ignore_prefix_size :, :].contiguous() target = target[:, self.ignore_prefix_size :].contiguous() # 函数返回对数概率和目标序列, return lprobs.view(-1, lprobs.size(-1)), target.view(-1) def compute_loss(self, model, net_output, sample, reduce=True): """ 用于计算给定模型在某个输入样本上的损失值。 """ # self 是指代类实例本身,model 是正在评估的模型,net_output 是给定输入样本时模型的输出,sample 是输入样本本身。 # 用于计算预测对数概率值和目标值 lprobs, target = self.get_lprobs_and_target(model, net_output, sample) # 用label_smoothed_nll_loss函数来计算损失值,该函数返回两个值,总损失值loss和负对数似然值。 loss, nll_loss = label_smoothed_nll_loss( lprobs, target, self.eps, ignore_index=self.padding_idx, # reduce 是一个布尔型标志,用于指示损失是否应该被缩减为一个标量值(例如,是否应该在批次上进行平均)。 reduce=reduce, ) return loss, nll_loss def compute_accuracy(self, model, net_output, sample): """ 计算神经网络模型在给定输入样本上预测准确率的方法。 """ # lprobs, target = self.get_lprobs_and_target(model, net_output, sample) # 通过target.ne(self.padding_idx)创建一个掩码张量。这会创建一个二进制掩码张量,其中每个元素为1,如果target中对应的元素不等于填充索引。 mask = target.ne(self.padding_idx) n_correct = torch.sum( lprobs.argmax(1).masked_select(mask).eq(target.masked_select(mask)) ) total = torch.sum(mask) return n_correct, total @classmethod def reduce_metrics(cls, logging_outputs) -> None: """Aggregate logging outputs from data parallel training.""" # 用于在数据并行训练时聚合日志输出。 # logging_outputs:这个参数包含了多个日志输出的列表 # 循环遍历logging_outputs列表,对于每个日志输出,统计它的loss,nll_loss、ntokens和sample_size指标的总和。 loss_sum = sum(log.get("loss", 0) for log in logging_outputs) nll_loss_sum = sum(log.get("nll_loss", 0) for log in logging_outputs) ntokens = sum(log.get("ntokens", 0) for log in logging_outputs) sample_size = sum(log.get("sample_size", 0) for log in logging_outputs) # 调用metrics.log_scalar和log_derived方法,将聚合后的指标值记录下来,其中log_scalar方法记录标量指标,如loss和nll_loss, log_derived()方法记录的是可以从已记录的指标派生出来的新指标,如ppl和accuracy。 metrics.log_scalar( "loss", loss_sum / sample_size / math.log(2), sample_size, round=3 ) metrics.log_scalar( "nll_loss", nll_loss_sum / ntokens / math.log(2), ntokens, round=3 ) metrics.log_derived( "ppl", lambda meters: utils.get_perplexity(meters["nll_loss"].avg) ) total = utils.item(sum(log.get("total", 0) for log in logging_outputs)) if total > 0: metrics.log_scalar("total", total) n_correct = utils.item( sum(log.get("n_correct", 0) for log in logging_outputs) ) metrics.log_scalar("n_correct", n_correct) metrics.log_derived( "accuracy", lambda meters: round( meters["n_correct"].sum * 100.0 / meters["total"].sum, 3 ) if meters["total"].sum > 0 else float("nan"), ) @staticmethod def logging_outputs_can_be_summed() -> bool: """ Whether the logging outputs returned by `forward` can be summed across workers prior to calling `reduce_metrics`. Setting this to True will improves distributed training speed. """ return True
五、使用到的环境
absl-py==1.0.0 antlr4-python3-runtime==4.8 anyio==3.4.0 argon2-cffi==21.1.0 attrs==21.2.0 Babel==2.9.1 backcall==0.2.0 bitarray==2.7.3 bleach==4.1.0 brotlipy==0.7.0 cachetools==4.2.4 certifi==2021.5.30 cffi @ file:///tmp/build/80754af9/cffi_1625807838443/work chardet @ file:///tmp/build/80754af9/chardet_1607706746162/work cmake==3.26.0 colorama==0.4.6 conda==4.10.3 conda-package-handling @ file:///tmp/build/80754af9/conda-package-handling_1618262148928/work cryptography @ file:///tmp/build/80754af9/cryptography_1616769286105/work cycler==0.11.0 Cython==0.29.33 debugpy==1.5.1 decorator==5.1.0 defusedxml==0.7.1 entrypoints==0.3 -e git+https://github.com/gpengzhi/Bi-SimCut@ab38d206413f32da45b6c0e0f9ee7f15d0d48a0f#egg=fairseq&subdirectory=fairseq filelock==3.10.0 fonttools==4.28.2 google-auth==2.3.3 google-auth-oauthlib==0.4.6 grpcio==1.42.0 hydra-core==1.0.7 idna @ file:///home/linux1/recipes/ci/idna_1610986105248/work importlib-metadata==4.8.2 importlib-resources==5.4.0 ipykernel==6.5.1 ipython==7.29.0 ipython-genutils==0.2.0 ipywidgets==7.6.5 jedi==0.18.1 Jinja2==3.0.3 json5==0.9.6 jsonschema==4.2.1 jupyter-client==7.1.0 jupyter-core==4.9.1 jupyter-server==1.12.0 jupyterlab==3.2.4 jupyterlab-language-pack-zh-CN==3.2.post2 jupyterlab-pygments==0.1.2 jupyterlab-server==2.8.2 jupyterlab-widgets==1.0.2 kiwisolver==1.3.2 lightseq==3.0.1 lit==15.0.7 lxml==4.9.2 Markdown==3.3.6 MarkupSafe==2.0.1 matplotlib==3.5.0 matplotlib-inline==0.1.3 mistune==0.8.4 mpmath==1.3.0 nbclassic==0.3.4 nbclient==0.5.9 nbconvert==6.3.0 nbformat==5.1.3 nest-asyncio==1.5.1 networkx==3.0 ninja==1.11.1 notebook==6.4.6 numpy==1.21.4 nvidia-cublas-cu11==11.10.3.66 nvidia-cuda-cupti-cu11==11.7.101 nvidia-cuda-nvrtc-cu11==11.7.99 nvidia-cuda-runtime-cu11==11.7.99 nvidia-cudnn-cu11==8.5.0.96 nvidia-cufft-cu11==10.9.0.58 nvidia-curand-cu11==10.2.10.91 nvidia-cusolver-cu11==11.4.0.1 nvidia-cusparse-cu11==11.7.4.91 nvidia-nccl-cu11==2.14.3 nvidia-nvtx-cu11==11.7.91 oauthlib==3.1.1 omegaconf==2.0.6 packaging==21.3 pandocfilters==1.5.0 parso==0.8.2 pexpect==4.8.0 pickleshare==0.7.5 Pillow==8.4.0 portalocker==2.7.0 prometheus-client==0.12.0 prompt-toolkit==3.0.22 protobuf==3.19.1 ptyprocess==0.7.0 pyasn1==0.4.8 pyasn1-modules==0.2.8 pycosat==0.6.3 pycparser @ file:///tmp/build/80754af9/pycparser_1594388511720/work Pygments==2.10.0 pyOpenSSL @ file:///tmp/build/80754af9/pyopenssl_1608057966937/work pyparsing==3.0.6 pyrsistent==0.18.0 PySocks @ file:///tmp/build/80754af9/pysocks_1605305779399/work python-dateutil==2.8.2 pytz==2021.3 PyYAML==6.0 pyzmq==22.3.0 regex==2022.10.31 requests @ file:///tmp/build/80754af9/requests_1608241421344/work requests-oauthlib==1.3.0 rsa==4.8 ruamel-yaml-conda @ file:///tmp/build/80754af9/ruamel_yaml_1616016699510/work sacrebleu==2.3.1 scipy==1.9.3 Send2Trash==1.8.0 setuptools-scm==6.3.2 six @ file:///tmp/build/80754af9/six_1623709665295/work sniffio==1.2.0 supervisor==4.2.2 sympy==1.11.1 tabulate==0.9.0 tensorboard==2.7.0 tensorboard-data-server==0.6.1 tensorboard-plugin-wit==1.8.0 tensorboardX==2.6 terminado==0.12.1 testpath==0.5.0 tomli==1.2.2 torch==1.10.1+cu113 torchaudio==0.10.1+cu113 torchvision==0.11.2+cu113 tornado==6.1 tqdm @ file:///tmp/build/80754af9/tqdm_1625563689033/work traitlets==5.1.1 triton==2.0.0 typing-extensions==4.0.0 urllib3 @ file:///tmp/build/80754af9/urllib3_1625084269274/work wcwidth==0.2.5 webencodings==0.5.1 websocket-client==1.2.1 Werkzeug==2.0.2 widgetsnbextension==3.5.2 zipp==3.6.0
您可以在Ubuntu上使用以下命令将当前Python环境中所有已安装的包及其版本信息导出到名为"requirements.txt"的文件中:
- pip freeze > requirements.txt
请注意,您需要在命令前加上pip关键字以使用pip命令。此命令将当前Python环境中所有已安装的包及其版本信息写入名为"requirements.txt"的文件中。
然后,您可以使用以下命令在另一个Python环境中安装这些依赖项:
- pip install -r requirements.txt
该命令将读取名为"requirements.txt"的文件中列出的所有依赖项并安装它们。请注意,您需要在执行此命令之前确保已安装Python和pip。
需要注意的是,如果您的Python应用程序依赖于系统级软件包,则建议您使用操作系统特定的包管理器来管理这些依赖项。
附录一:熵以及信息熵
熵:用于描述不确定性,表示系统混乱的程度,越整齐熵也就越小,越混乱不确定的程度越大,熵也就越大,因此整个环境会自发的朝着混乱的方向发展,也就是熵增原理。
信息熵含义:信息熵表示随机变量不确定的程度。一件事情发生的概率越高,那么他的确定性也就越大,那么它的熵也就越小。信息熵常常被作为一个系统的信息含量的量化指标。
性质:信息熵非负。当一件事发生的概率为1时,信息就没有不确定,那么它的熵就是0。
公式:p(x)代表的是事件x发生的概率
总结:那些接近确定性的分布(输出几乎可以确定)具有较低的熵,那些接近均匀分布的概率分布具有较高的熵。
附录二:KL散度(相对熵)
定义:在机器学习领域,KL散度用来度量两个函数(概率分布)的相似程度或者相近程度,是用来描述两个概率分布差异的一种方法,也叫做相对熵。也就是说KL散度可以作为一种损失,来计算两者之间的概率差异。
公式:
性质:
- KL散度的值始终>=0,当且仅当P(x)=Q(x)时等号成立。
- KL散度并不是一个对称量,KL(p||q)不等于KL(q||p)
双向KL散度定义:通过交换这两种分布的位置以间接使用整体对称的KL散度。
附录三:JS散度
定义:KL散度是不对称的,训练神经网络会因为不同的顺序造成不一样的训练结果,为了克服这个问题,提出了JS散度。
性质:
- JS散度的值域范围是[0,1],相同为0,相反则为1,相比于KL,对相似度的判断更加准确了。
- JS散度是一个对称量,JS(p||q)等于JS(q||p), 对称可以让散度度量更加准确,下边是证明代码
import numpy as np import math # 离散随机变量的KL散度和JS散度的计算方法 def KL(p, q): # p,q为两个list,里面存着对应的取值的概率,整个list相加为1 if 0 in q: raise ValueError return sum(_p * math.log(_p / _q) for (_p, _q) in zip(p, q) if _p != 0) def JS(p, q): M = [0.5 * (_p + _q) for (_p, _q) in zip(p, q)] return 0.5 * (KL(p, M) + KL(q, M)) def exp(a, b): a = np.array(a, dtype=np.float32) b = np.array(b, dtype=np.float32) a /= a.sum() b /= b.sum() print(a) print(b) print(KL(a, b)) print(JS(a, b)) if __name__ == '__main__': # exp1 print('exp1: Start') print(exp([1, 2, 3, 4, 5], [5, 4, 3, 2, 1])) print('exp1: End') # exp2 # 把公式中的第二个分布做修改,假设这个分布中有某个值的取值非常小,就有可能增加两个分布的散度值 print('exp2: Start') print(exp([1, 2, 3, 4, 5], [1e-12, 4, 3, 2, 1])) print(exp([1, 2, 3, 4, 5], [5, 4, 3, 2, 1e-12])) print('exp2: End') # exp3 print('exp3: Start') print(exp([1e-12,2,3,4,5],[5,4,3,2,1])) print(exp([1,2,3,4,1e-12],[5,4,3,2,1])) print('exp3: End')
输出:
exp1: Start
[0.06666667 0.13333334 0.2 0.26666668 0.33333334]
[0.33333334 0.26666668 0.2 0.13333334 0.06666667]
0.5216030835963031
0.11968758856917597
None
exp1: End
exp2: Start
[0.06666667 0.13333334 0.2 0.26666668 0.33333334]
[1.e-13 4.e-01 3.e-01 2.e-01 1.e-01]
2.065502018456509
0.0985487692550548
None
[0.06666667 0.13333334 0.2 0.26666668 0.33333334]
[3.5714287e-01 2.8571430e-01 2.1428572e-01 1.4285715e-01 7.1428574e-14]
9.662950847122168
0.19399530008415986
None
exp2: End
exp3: Start
[7.1428574e-14 1.4285715e-01 2.1428572e-01 2.8571430e-01 3.5714287e-01]
[0.33333334 0.26666668 0.2 0.13333334 0.06666667]
0.7428131560123377
0.19399530008415986
None
[1.e-01 2.e-01 3.e-01 4.e-01 1.e-13]
[0.33333334 0.26666668 0.2 0.13333334 0.06666667]
0.38315075574389773
0.0985487692550548
None
exp3: End
将第一个实验与第二个实验做对比,可以看出KL散度的波动比较大,而JS的波动相对小。
如果将第二个实验和第三个实验做对比,可以发现KL散度在衡量两个分布的差异时具有很大的不对称性。如果后面的分布在某一个值上缺失,就会得到很大的散度值;但是如果前面的分布在某一个值上缺失,最终的KL散度并没有太大的波动。这个demo可以清楚地看出KL不对称性带来的一些小问题,而JS具有对称性,所以第二个实验和第三个实验的JS散度实际上是距离相等的分布组。
附录四:互信息
定义:互信息衡量的是两种度量间相互关联的程度,极端一点来理解,如果X,Y相互独立,那么互信息为0,因为两者不相关;而如果X,Y相互的关系确定(比如Y是X的函数),那么此时X,Y是“完全关联的”。
公式:
总结
好烦,又想到了被论文支配的恐惧😱。