当前位置: 首页 > 编程日记 > 正文

三两下实现NLP训练和预测,这四个框架你要知道

640?wx_fmt=jpeg

作者 | 狄东林 刘元兴 朱庆福 胡景雯

编辑 | 刘元兴,崔一鸣

来源 | 哈工大SCIR(ID:HIT_SCIR)


引言

随着人工智能的发展,越来越多深度学习框架如雨后春笋般涌现,例如PyTorch、TensorFlow、Keras、MXNet、Theano 和 PaddlePaddle 等。这些基础框架提供了构建一个模型需要的基本通用工具包。但是对于 NLP 相关的任务,我们往往需要自己编写大量比较繁琐的代码,包括数据预处理和训练过程中的工具等。因此,大家通常基于 NLP 相关的深度学习框架编写自己的模型,如 OpenNMT、ParlAI 和 AllenNLP 等。借助这些框架,三两下就可以实现一个 NLP 相关基础任务的训练和预测。但是当我们需要对基础任务进行改动时,又被代码封装束缚,举步维艰。因此,本文主要针对于如何使用框架实现自定义模型,帮助大家快速了解框架的使用方法。

我们首先介绍广泛用于NLP/CV领域的TensorFlow框架——Tensor2Tensor,该框架提供了 NLP/CV 领域中常用的基本模型。然后介绍NLP 领域的 AllenNLP 框架,该框架基于 PyTorch 平台开发,为 NLP 模型提供了统一的开发架构。接着在介绍 NLP 领域中重要的两个子领域,神经机器翻译和对话系统常用的框架,OpenNMT 和 ParlAI 。通过这四个框架的介绍,希望能帮助大家了解不同开发平台,不同领域下的 NLP 框架的使用方式。

640?wx_fmt=png

一、Tensor2Tensor

640?wx_fmt=png

Tensor2Tensor[1] 是一个基于 TensorFlow 的较为综合性的库,既包括一些CV 和 NLP 的基本模型,如 LSTM,CNN等,也提供一些稍微高级一点的模型,如各式各样的 GAN 和 Transformer 。对 NLP 的各项任务支持得都比较全面,很方便容易上手。

由于该资源库仍处于不断开发过程中,截止目前为止,已经有 3897 次 commit,66 个 release 版本,178 contributors 。在 2018 年《Attention is all you need》这个全网热文中,该仓库是官方提供的 Transformer 模型版本,后面陆陆续续其余平台架构才逐渐补充完成。

Tensor2Tensor(Transformer)使用方法

注意:有可能随着版本迭代更新的过程中会有局部改动

安装环境

1. 安装 CUDA 9.0 (一定是9.0,不能是9.2)

2. 安装 TensorFlow (现在是1.12)

3. 安装 Tensor2Tensor (参考官网安装)

开始使用

1. 数据预处理

这一步骤是根据自己任务自己编写一些预处理的代码,比如字符串格式化,生成特征向量等操作。

2. 编写自定义 problem:

  • 编写自定义的 problem 代码,一定需要在自定义类名前加装饰器(@registry.registry_problem)。

  • 自定义 problem 的类名一定是驼峰式命名,py 文件名一定是下划线式命名,且与类名对应。

  • 一定需要继承父类 problem,t2t 已经提供用于生成数据的 problem,需要自行将自己的问题人脑分类找到对应的父类,主要定义的父类 problem有:(运行 t2t-datagen 可以查看到 problem list )。

  • 一定需要在__init__.py文件里导入自定义problem文件。

3. 使用 t2t-datagen 将自己预处理后的数据转为 t2t 的格式化数据集【注意路径】

  • 运行 t2t-datagen --help 或 t2t-datagen --helpfull。例如:

cd scripts && t2t-datagen --t2t_usr_dir=./ --data_dir=../train_data --tmp_dir=../tmp_data --problem=my_problem

  • 如果自定义 problem 代码的输出格式不正确,则此命令会报错


4. 使用 t2t-trainer 使用格式化的数据集进行训练

  • 运行 t2t-trainer --help 或 t2t-trainer --helpfull 。例如:

1cd scripts && t2t-datagen --t2t_usr_dir=./ --data_dir=../train_data --tmp_dir=../tmp_data --problem=my_problem

5. 使用 t2t-decoder 对测试集进行预测【注意路径】

  • 如果想使用某一个 checkpoint 时的结果时,需要将 checkpoint 文件中的第一行: model_checkpoint_path: “model.ckpt-xxxx” 的最后的序号修改即可。例如:

cd scripts && t2t-decoder --t2t_usr_dir=./ --problem=my_problem --data_dir=../train_data --model=transformer --hparams_set=transformer_base --output_dir=../output --decode_hparams=”beam_size=5,alpha=0.6” --decode_from_file=../decode_in/test_in.txt --decode_to_file=../decode_out/test_out.txt

6. 使用 t2t-exporter 导出训练模型

7. 分析结果

附: (整体代码)

# coding=utf-8	
from tensor2tensor.utils import registry	from tensor2tensor.data_generators import problem, text_problems	@registry.register_problem	class AttentionGruFeature(text_problems.Text2ClassProblem):	ROOT_DATA_PATH = '../data_manager/'	PROBLEM_NAME = 'attention_gru_feature'	@property	def is_generate_per_split(self):	return True	@property	def dataset_splits(self):	return [{	"split": problem.DatasetSplit.TRAIN,	"shards": 5,	}, {	"split": problem.DatasetSplit.EVAL,	"shards": 1,	}]	@property	def approx_vocab_size(self):	return 2 ** 10  # 8k vocab suffices for this small dataset.	@property	def num_classes(self):	return 2	@property	def vocab_filename(self):	return self.PROBLEM_NAME + ".vocab.%d" % self.approx_vocab_size	def generate_samples(self, data_dir, tmp_dir, dataset_split):	del data_dir	del tmp_dir	del dataset_split	# with open('{}self_antecedent_generate_sentences.pkl'.format(self.ROOT_DATA_PATH), 'rb') as f:	#     # get all the sentences for antecedent identification	#     _sentences = pickle.load(f)	#	# for _sent in _sentences:	#     # # sum pooling, FloatTensor, Size: 400	#     # _sent.input_vec_sum	#     # # sum pooling with feature, FloatTensor, Size: 468	#     # _sent.input_vec_sum_feature	#     # # GRU, FloatTensor, Size: 6100	#     # _sent.input_vec_hidden	#     # # GRU with feature, FloatTensor, Size: 6168	#     # _sent.input_vec_hidden_feature	#     # # AttentionGRU, FloatTensor, Size: 1600	#     # _sent.input_vec_attention	#     # # AttentionGRU with feature, FloatTensor, Size: 1668	#     # _sent.input_vec_attention_feature	#     # # tag(1 for positive case, and 0 for negative case), Int, Size: 1	#     # _sent.antecedent_label	#     # # tag(1 for positive case, and 0 for negative case), Int, Size: 1	#     # _sent.trigger_label	#     # # trigger word for the error analysis, Str	#     # _sent.trigger	#     # # trigger word auxiliary type for the experiment, Str	#     # _sent.aux_type	#     # # the original sentence for the error analysis, Str	#     # _sent.sen	#	#     yield {	#         "inputs": _sent.input_vec_attention_feature,	#         "label": _sent.antecedent_label	#     }	with open('../prep_ante_data/antecedent_label.txt') as antecedent_label, open(	'../prep_ante_data/input_vec_attention_gru_feature.txt') as input_vec:	for labal in antecedent_label:	yield {	"inputs": input_vec.readline().strip()[1:-2],	"label": int(labal.strip())	}	antecedent_label.close()	input_vec.close()	# PROBLEM_NAME='attention_gru_feature'	
# DATA_DIR='../train_data_atte_feature'	
# OUTPUT_DIR='../output_atte_feature'	
# t2t-datagen --t2t_usr_dir=. --data_dir=$DATA_DIR --tmp_dir=../tmp_data --problem=$PROBLEM_NAME	
# t2t-trainer --t2t_usr_dir=. --data_dir=$DATA_DIR --problem=$PROBLEM_NAME --model=transformer --hparams_set=transformer_base --output_dir=$OUTPUT_DIR

Tensor2Tensor使用总结

T2T 是Google 非官方提供的仓库,是社区广大爱好者共同努力建设的简单入门型框架,底层封装TF,能满足大部分CV 和 NLP的任务,很多主流成熟的模型也已经都有实现。直接继承或实现一些框架内预设的接口,就可以完成很多任务。入门起来非常友好,并且文档更新也较为及时。认真阅读文档(或阅读报错信息)就可以了解并使用该框架,方便许多非大幅创新模型的复现。

二、AllenNLP

640?wx_fmt=png

AllenNLP 是一个基于 PyTorch 的 NLP 研究库,可为开发者提供语言任务中的各种业内最佳训练模型。官网提供了一个很好的入门教程[2],能够让初学者在 30 分钟内就了解 AllenNLP 的使用方法。

AllenNLP 使用方法

由于 AllenNLP 已经帮我们实现很多麻烦琐碎的预处理和训练框架,我们实际需要编写的只有:

1. DatasetReader

DatasetReader 的示例代码如下所示。

from typing import Dict, Iterator	from allennlp.data import Instance	from allennlp.data.fields import TextField	from allennlp.data.dataset_readers import DatasetReader	from allennlp.data.token_indexers import TokenIndexer, SingleIdTokenIndexer	from allennlp.data.tokenizers import WordTokenizer, Tokenizer	@DatasetReader.register('custom')	class CustomReader(DatasetReader):	def __init__(self, tokenizer: Tokenizer = None, token_indexers: Dict[str, TokenIndexer] = None) -> None:	super().__init__(lazy=False)	self.tokenizer = tokenizer or WordTokenizer()	self.word_indexers = token_indexers or {"word": SingleIdTokenIndexer('word')}	def text_to_instance(self, _input: str) -> Instance:	fields = {}	tokenized_input = self.tokenizer.tokenize(_input)	fields['input'] = TextField(tokenized_input, self.word_indexers)	return Instance(fields)	def _read(self, file_path: str) -> Iterator[Instance]:	with open(file_path) as f:	for line in f:	yield self.text_to_instance(line)

首先需要自定义_read函数,写好读取数据集的方式,通过yield方式返回构建一个instance需要的文本。然后通过text_to_instance函数将文本转化为instance。在text_to_instance函数中,需要对输入的文本进行切分,然后构建fileld

self.tokenizer是用来切分文本成 Token 的。有 Word 级别的也有 Char级别的。self.word_indexers是用来索引 Token 并转换为 Tensor。同样 TokenIndexer 也有很多种,在实现自己的模型之前可以看看官方文档有没有比较符合自己需要的类型。如果你需要构建多个 Vocabulary ,比如源语言的vocab 和目标语言的vocab, 就需要在这里多定义一个self.word_indexers。不同indexers在vocab中,是通过SingleIdTokenIndexer 函数初始化的 namespace 来区分的,也就是 15 行代码中最后一个的'word'

2. Model

与 PyTorch 实现 model 的方式一样,但需要注意的是:

@Model.register('') 注册之后可以使用 JsonNet 进行模型选择(如果你有多个模型,可以直接修改 Json 值来切换,不需要手动修改代码)。

由于 AllenNLP 封装了 Trainer ,所以我们需要在 model 内实现或者选择已有的评价指标,这样在训练过程中就会自动计算评价指标。具体方法是,在__init__方法中定义评价函数,可以从在官方文档[3]上看看有没有,如果没有的话就需要自己写。

self.acc = CategoricalAccuracy()

然后在forward方法中调用评价函数计算指标

self.acc(output, labels)

最后在 model 的get_metrics返回对应指标的 dict 结果就行了。

def get_metrics(self, reset: bool = False) -> Dict[str, float]:	return {"acc": self.acc.get_metric(reset)}

3. Trainer

一般来说直接调用 AllenNLP 的 Trainer 方法就可以自动开始训练了。但是如果你有一些特殊的训练步骤,比如 GAN[4],你就不能单纯地使用 AllenNLP 的 Trainer,得把 Trainer 打开进行每步的迭代,可以参考[4]中 trainer 的写法。

AllenNLP使用总结

关于 AllenNLP 的学习代码,可以参考[5]。由于 AllenNLP 是基于 PyTorch 的,代码风格和 PyTorch 的风格基本一致,因此如果你会用 PyTorch,那上手 AllenNLP 基本没有什么障碍。代码注释方面也比较全,模块封装方面比较灵活。AllenNLP 的代码非常容易改动,就像用纯的 PyTorch 一样灵活。当然灵活也就意味着很多复杂的实现,AllenNLP 目前还没有,大部分可能都需要自己写。AllenNLP 依赖了很多 Python 库,近期也在更新。


三、OpenNMT

640?wx_fmt=png

OpenNMT[6] 是一个开源的神经机器翻译(neural machine translation)项目,采用目前普遍使用的编码器-解码器(encoder-decoder)结构,因此,也可以用来完成文本摘要、回复生成等其他文本生成任务。目前,该项目已经开发出 PyTorch、TensorFlow 两个版本,用户可以按需选取。本文以 PyTorch 版本[7]为例进行介绍。

OpenNMT使用方法

1. 数据处理

作为一个典型的机器翻译框架,OpenNMT 的数据主要包含 source 和 target 两部分,对应于机器翻译中的源语言输入和目标语言翻译。OpenNMT 采用 TorchText 中的 Field 数据结构来表示每个部分。用户自定义过程中,如需添加 source 和 target 外的其他数据,可以参照 source field 或 target field 的构建方法,如构建一个自定义的 user_data 数据:

fields["user_data"] = torchtext.data.Field(	init_token=BOS_WORD, eos_token=EOS_WORD,	pad_token=PAD_WORD,	include_lengths=True)

其中 init_token、eos_token 和 pad_token 分别为用户自定义的开始字符、结束字符和 padding 字符。Include_lengths 为真时,会同时返回处理后数据和数据的长度。

2. 模型

OpenNMT 实现了注意力机制的编码器-解码器模型。框架定义了编码器和解码器的接口,在该接口下,进一步实现了多种不同结构的编码器解码器,可供用户按需组合,如 CNN、 RNN 编码器等。如用户需自定义特定结构的模块,也可以遵循该接口进行设计,以保证得到的模块可以和 OpenNMT 的其他模块进行组合。其中,编码器解码器接口如下:

class EncoderBase(nn.Module):	def forward(self, input, lengths=None, hidden=None):	raise NotImplementedError	class RNNDecoderBase(nn.Module):	def forward(self, input, context, state, context_lengths=None):	raise NotImplementedError

3. 训练

OpenNMT 的训练由 Trainer.py 中 Trainer 类控制,该类的可定制化程度并不高,只实现了最基本的序列到序列的训练过程。对于多任务、对抗训练等复杂的训练过程,需要对该类进行较大的改动。

OpenNMT使用总结

OpenNMT 提供了基于 PyTorch 和 TensorFlow 这两大主流框架的不同实现,能够满足绝大多数用户的需求。对于基础框架的封装使得其丧失了一定的灵活性,但是对于编码器-解码器结构下文本生成的任务来说,可以省去数据格式、接口定义等细节处理,将精力更多集中在其自定义模块上,快速搭建出需要的模型。


四、ParlAI

640?wx_fmt=png

ParlAI 是 Facebook 公司开发出的一个专注于对话领域在很多对话任务上分享,训练和评估对话模型的平台[8]。这个平台可以用于训练和测试对话模型,在很多数据集上进行多任务训练,并且集成了 Amazon Mechanical Turk,以便数据收集和人工评估。

ParlAI 中的基本概念:

  • world 定义了代理彼此交互的环境。世界必须实施一种 parley 方法。每次对 parley 的调用都会进行一次交互,通常每个代理包含一个动作。

  • agent 可以是一个人,一个简单的机器人,可以重复它听到的任何内容,完美调整的神经网络,读出的数据集,或者可能发送消息或与其环境交互的任何其他内容。代理有两个他们需要定义的主要方法:

def observe(self, observation): #用观察更新内部状态	
def act(self): #根据内部状态生成动作
  • observations 是我们称之为代理的 act 函数返回的对象,并且因为它们被输入到其他代理的 observe 函数而被命名。这是 ParlAI 中代理与环境之间传递消息的主要方式。观察通常采用包含不同类型信息的 python 词典的形式。

  • teacher 是特殊类型的代理人。他们像所有代理一样实施 act 和 observe 功能,但他们也会跟踪他们通过报告功能返回的指标,例如他们提出的问题数量或者正确回答这些问题的次数。

ParlAI 的代码包含如下几个主要的文件夹[9]:

  • core 包含框架的主要代码;

  • agents 包含可以和不同任务交互的代理;

  • examples 包含不同循环的一些基本示例;

  • tasks 包含不同任务的代码;

  • mturk 包含设置 Mechanical Turk 的代码及 MTurk 任务样例。

ParlAI 使用方法

ParlAI 内部封装了很多对话任务(如 ConvAI2 )和评测(如 F1 值和 hits@1 等等)。使用 ParlAI 现有的数据,代码以及模型进行训练和评测,可以快速实现对话模型中的很多 baseline 模型。但由于代码封装性太强,不建议使用它从头搭建自己的模型。想在基础上搭建自己的模型可以详细参考官网中的教程[10]。

这里简单介绍直接利用内部的数据,代码以及模型进行训练和评测的一个简单例子(Train a Transformer on Twitter):

1. 打印一些数据集中的例子

python examples/display_data.py -t twitter	
*# display first examples from twitter dataset*

2. 训练模型

python examples/train_model.py -t twitter -mf /tmp/tr_twitter -m transformer/ranker -bs 10 -vtim 3600 -cands batch -ecands batch --data-parallel True	
# train transformer ranker

3. 评测之前训练出的模型

python examples/eval_model.py -t twitter -m legacy:seq2seq:0 -mf models:twitter/seq2seq/twitter_seq2seq_model	
# Evaluate seq2seq model trained on twitter from our model zoo

4. 输出模型的一些预测

python examples/display_model.py -t twitter -mf /tmp/tr_twitter -ecands batch	
# display predictions for model saved at specific file on twitter

ParlAI 使用总结

ParlAI 有自己的一套模式,例如 world、agent 和 teacher 等等。代码封装性特别好,代码量巨大,如果想查找一个中间结果,需要一层一层查看调用的函数,不容易进行修改。ParlAI 中间封装了很多现有的 baseline 模型,对于对话研究者,可以快速实现 baseline 模型。目前 ParlAI 还在更新,不同版本之间的代码可能结构略有不同,但是 ParlAI 的核心使用方法大致相同。

五、总结

本文介绍了四种常见框架构建自定义模型的方法。Tensor2Tensor 涵盖比较全面,但是只支持 TensorFlow。AllenNLP 最大的优点在于简化了数据预处理、训练和预测的过程。代码改起来也很灵活,但是一些工具目前官方还没有实现,需要自己写。如果是比较传统的编码器-解码器结构下文本生成任务,使用 OpenNMT 能节省很多时间。但是如果是结构比较新颖的模型,使用 OpenNMT 搭建模型依旧是一个不小的挑战。ParlAI 内部封装了很多对话任务,方便使用者快速复现相关的 baseline 模型。但由于代码封装性太强和其特殊的模式,使用 ParlAI 从头搭建自己的模型具有一定的挑战性。每个框架都有各自的优点和弊端,大家需结合自身情况和使用方式进行选择。但是不建议每个框架都试一遍,毕竟掌握每个框架还是需要一定时间成本的。

参考资料

[1] https://github.com/tensorflow/tensor2tensor

[2] https://allennlp.org/tutorials

[3]https://allenai.github.io/allennlp-docs/api/allennlp.training.metrics.html

[4] http://www.realworldnlpbook.com/blog/training-a-shakespeare-reciting-monkey-using-rl-and-seqgan.html

[5] https://github.com/mhagiwara/realworldnlp

[6] http://opennmt.net/

[7] https://github.com/OpenNMT/OpenNMT-py

[8]http://parl.ai.s3-website.us-east-2.amazonaws.com/docs/tutorial_quick.html

[9] https://www.infoq.cn/article/2017/05/ParlAI-Facebook-AI

[10]http://parl.ai.s3-website.us-east-2.amazonaws.com/docs/tutorial_basic.html

(*本文为 AI 科技大本营转载文章,转载请联系原作者)

福利时刻

入群参与每周抽奖~

扫码添加小助手,回复:大会,加入福利群,参与抽奖送礼!

640?wx_fmt=jpeg

AI ProCon 大会优惠票限时抢购中!识别海报二维码,即刻购票~

640?wx_fmt=jpeg

推荐阅读

  • IBM重磅开源Power芯片指令集?国产芯迎来新机遇?

  • KDD 2019高维稀疏数据上的深度学习Workshop论文汇总

  • 说出来你可能不信,现在酒厂都在招算法工程师

  • 姚班三兄弟3万块创业八年,旷视终冲刺港股

  • 2019 AI ProCon日程出炉:Amazon首席科学家李沐亲授「深度学习」

  • AI Top 30+案例评选等你来秀!

  • 福利 | 马上为你安排和大咖面对面交流的机会,不可错过

  • 92年小哥绞尽脑汁骗得价值800万比特币, 破案后警方决定还给受害者

  • 他是叶问制片人也是红色通缉犯, 他让泰森卷入ICO, 却最终演变成了一场狗血的罗生门……


640?wx_fmt=png

你点的每个“在看”,我都认真当成了喜欢

相关文章:

大学计算机基础实验

下载2013算法实验报告.rar转载于:https://www.cnblogs.com/shajianheng/p/3381968.html

java基础(十三)-----详解内部类——Java高级开发必须懂的

java基础(十三)-----详解内部类——Java高级开发必须懂的 目录 为什么要使用内部类内部类基础静态内部类 成员内部类 成员内部类的对象创建继承成员内部类局部内部类推荐博客匿名内部类正文 可以将一个类的定义放在另一个类的定义内部,这就是内部类。 回到顶部为什么…

C++中函数指针的使用

A function pointer is a variable that stores the address of a function that can later be called through that function pointer. This is useful because functions encapsulate behavior.函数指针是一个指向函数的指针,函数指针表示一个函数的入口地址。指针是变量&…

只做好CTR预估远不够,淘宝融合CTR、GMV、收入等多目标有绝招

作者 | 吴海波转载自知乎用户吴海波【导读】一直以来,电商场景就存在 ctr、cvr、gmv、成交 uv 等多个目标,都是核心指标。理想情况下,提升 ctr 就能提升 gmv,但本文作者认为,在一定程度上, ctr 和 gmv 并不…

Android监听HOME按键

2019独角兽企业重金招聘Python工程师标准>>> <!-- lang: java --> class HomeKeyEventBroadCastReceiver extends BroadcastReceiver {static final String SYSTEM_REASON "reason";static final String SYSTEM_HOME_KEY "homekey";// …

OpenCV代码提取:merge/split函数的实现

对OpenCV中的merge/split函数进行了实现&#xff0c;经测试&#xff0c;与OpenCV3.1结果完全一致。merge实现代码merge.hpp&#xff1a;// fbc_cv is free software and uses the same licence as OpenCV // Email: fengbingchun163.com#ifndef FBC_CV_MERGE_HPP_ #define FBC_…

DeepMind提图像生成的递归神经网络DRAW,158行Python代码复现

作者 | Samuel Noriega译者 | Freesia编辑 | 夕颜出品 | AI科技大本营&#xff08;ID: rgznai100&#xff09;【导读】最近&#xff0c;谷歌 DeepMInd 发表论文( DRAW: A Recurrent Neural Network For Image Generation&#xff09;&#xff0c;提出了一个用于图像生成的递归神…

其他进制的数字

JS中如果需要表示16进制的数字,则需要以0X开头 0X10 八进制数字以0开头 070 070有些浏览器会以8进制解析,但是有些则用10进制解析,10进制为70,8进制为56 所以parseint() 第二个参数可以设定进制,比如 parseint(“070”,10)代表以10进制解析070 2进制以0b开头,但是不是所有浏览…

java中的移位运算符

移位运算符是在数字的二进制形式上进行平移。主要有左移&#xff08;<<&#xff09;、带符号右移&#xff08;>>&#xff09;以及无符号右移&#xff08;>>>&#xff09;。左移运算符&#xff08;<<&#xff09;的运算规则为&#xff1a;按二进制形…

C++11中nullptr的使用

在C语言中&#xff0c;NULL实际上是一个void* 的指针&#xff0c;然后把void* 指针赋值给其它类型的指针的时候&#xff0c;会隐式转换成相应的类型。而如果用一个C编译器来编译的时候是要出错的&#xff0c;因为C是强类型的&#xff0c;void* 是不能隐式转换成其它指针类型的。…

埃森哲、亚马逊和万事达卡抱团推出的区块链项目有何神通?

据外媒报道&#xff0c;今日埃森哲宣布了一项新的区块链项目&#xff0c;该项目为基于区块链的循环供应链&#xff0c;将与万事达卡和亚马逊共同合作。据官方介绍&#xff0c;这个基于区块链的循环供应链能够让客户识别供应链上的小规模供应商和种植者&#xff0c;例如&#xf…

小团队如何玩转物联网开发?

近几年来&#xff0c;物联网发展迅速&#xff1a;据中商产业研究院《2016——2021年中国物联网产业市场研究报告》显示&#xff0c;预计到2020年&#xff0c;中国物联网的整体规模将达2.2万亿元&#xff0c;产业规模比互联网大30倍。与之相反的是&#xff0c;物联网开发者在开发…

Build Boost C++ libraries for x32/x64 VC++ compilers on Windows

2019独角兽企业重金招聘Python工程师标准>>> Boost is a set of libraries for the C programming language that provide support for tasks and structures such as linear algebra, pseudorandom number generation, multithreading, image processing, regular …

C++11中auto的使用

在C语言中&#xff0c;就有了auto关键字&#xff0c;它被当作是一个变量的存储类型修饰符&#xff0c;表示自动变量(局部变量)。它不能被单独使用&#xff0c;否则编译器会给出警告。在C11标准中&#xff0c;添加了新的类型推导特性。在C 11中&#xff0c;使用auto定义的变量不…

攻和防谁更厉害?AI技术在恶意软件检测中的应用和对抗

AI技术的发展为网络安全带来新机遇的同时&#xff0c;黑客也在逐渐利用AI漏洞建立对抗样本以躲避攻击&#xff0c;双方在各自领域的更多尝试也将是AI技术发展的一场新博弈。那么&#xff0c;在应用中&#xff0c;如何利用AI检测技术与恶意软件展开对抗&#xff1f; 腾讯安全技术…

一文看懂机器学习中的常用损失函数

作者丨stephenDC编辑丨zandy来源 | 大数据与人工智能&#xff08;ID: ai-big-data&#xff09;导语&#xff1a;损失函数虽然简单&#xff0c;却相当基础&#xff0c;可以看做是机器学习的一个组件。机器学习的其他组件&#xff0c;还包括激活函数、优化器、模型等。本文针对机…

Using Apache2 with JBoss AS7 on Ubuntu

大体思路同《Using Apache Web Server with Jboss AS 7》一致&#xff0c;但在Ubuntu上的操作与之前有些区别。 这里仍然演示mod_proxy的配置。 首先加载相应的模块。Ubuntu中加载模块和卸载模块均可以通过命令操作&#xff0c;与其对应的命令分别是a2enmod和a2dismod。 启用…

OpenCV代码提取:rotate函数的实现

OpenCV中并没有直接提供实现rotate的函数&#xff0c;这里通过getRotationMatrix2D和warpAffine函数实现rotate&#xff0c;并增加了一个crop参数&#xff0c;用来判断是否进行crop。目前支持uchar和float两种类型&#xff0c;经测试&#xff0c;与OpenCV3.1结果完全一致。公式…

在 Node.js 中用子进程操作标准输入/输出

翻译&#xff1a;疯狂的技术宅原文&#xff1a;http://2ality.com/2018/05/chi... 本文首发微信公众号&#xff1a;jingchengyideng欢迎关注&#xff0c;每天都给你推送新鲜的前端技术文章 在本中&#xff0c;我们在 Node.js 中把 shell 命令作为子进程运行。然后异步读取这些进…

再见,Python 2.x

整理 | 屠敏来源 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;在技术的长河中&#xff0c;软件、工具、系统等版本的迭代本是常事&#xff0c;但由于使用习惯、版本的兼容性、易用性等因素&#xff0c;很多用户及开发者在使用或做开发的过程中&#xff0c;并不愿意及…

Android UI系列-----CheckBox和RadioButton(1)

主要记录一下CheckBox多选框和RadioGroup、RadioButton单选框的设置以及注册监听器 1.CheckBox 布局文件&#xff1a; <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"android…

C++中struct的使用

C语言继承了C语言的struct&#xff0c;并且加以扩充。在C语言中struct是只能定义数据成员&#xff0c;而不能定义成员函数的。而在C中&#xff0c;struct类似于class&#xff0c;在其中既可以定义数据成员&#xff0c;又可以定义成员函数。结构类型是用户定义的复合类型&#x…

填报表中也可以添加 html 事件

在实际的项目开发中&#xff0c;填报表的应用十分广泛。 多数情况下&#xff0c;填报表会作为整个项目的一部分配合需求灵活使用&#xff0c;但有时也会受大项目环境的影响&#xff0c;产生一些特别的要求。比如&#xff0c;通常报表单元格的数据类型大多是文本&#xff0c;有时…

60+业内技术专家,9大核心技术专题,AI ProCon倒计时一周!

2018 年&#xff0c;由 CSDN 举办的第一届 AI 开发者大会喊出“只讲技术&#xff0c;拒绝空谈”&#xff0c;两天会议时间&#xff0c;国内外几十家顶尖科技企业讲述了其主流技术及其应用案例&#xff0c;真正引领国内开发者紧跟技术浪潮。一年过去&#xff0c;在你还未有所觉察…

密码学研究-数字签名

引入&#xff1a;提到签名&#xff0c;大家都不陌生&#xff0c;大家知道&#xff0c;重大的文件一般都要领导签名&#xff0c;来确保这个文件的真实有效。而一些比较重要的合同&#xff0c;比如买房的购房合同&#xff0c;都要盖“骑缝章”&#xff0c;这个骑缝章&#xff0c;…

C++11中shared_ptr的使用

在C中&#xff0c;动态内存的管理是通过一对运算符来完成的&#xff1a;new&#xff0c;在动态内存中为对象分配空间并返回一个指向该对象的指针&#xff0c;可以选择对对象进行初始化&#xff1b;delete&#xff0c;接受一个动态对象的指针&#xff0c;销毁该对象&#xff0c;…

colly源码学习

colly源码学习 colly是一个golang写的网络爬虫。它使用起来非常顺手。看了一下它的源码&#xff0c;质量也是非常好的。本文就阅读一下它的源码。 使用示例 func main() {c : colly.NewCollector()// Find and visit all linksc.OnHTML("a[href]", func(e *colly.HTM…

可惜了,你们只看到“双马会”大型尬聊

作者 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;导读&#xff1a;2019 年 8 月 29 日&#xff0c;世界人工智能大会&#xff08;WAIC&#xff09;在上海正式拉开帷幕。开幕式上&#xff0c;最让人瞩目的莫过于阿里巴巴前 CEO 马云与特斯拉 CEO Elon Musk …

Java 过滤特殊字符的 正则表达式

Java正则表达式学习&#xff1a; 因为正则表达式是一个很庞杂的体系&#xff0c;此例仅举些入门的概念&#xff0c;更多的请参阅相关书籍及自行摸索。 \\ 反斜杠 \t 间隔 (\u0009) \n 换行 (\u000A) \r 回车 (\u000D) \d 数字 等价于[0-9] \D 非数字 等价于[^0-9] \s 空…

C++11中unique_ptr的使用

在C中&#xff0c;动态内存的管理是通过一对运算符来完成的&#xff1a;new&#xff0c;在动态内存中为对象分配空间并返回一个指向该对象的指针&#xff0c;可以选择对对象进行初始化&#xff1b;delete&#xff0c;接受一个动态对象的指针&#xff0c;销毁该对象&#xff0c;…