关闭

Tensorflow 自动文摘: 基于Seq2Seq+Attention模型的Textsum模型

5488人阅读 评论(23) 收藏 举报
分类:

from:

Tensorflow 自动文摘: 基于Seq2Seq+Attention模型的Textsum模型


 Github下载完整代码

https://github.com/rockingdingo/deepnlp#textsum


简介

这篇文章中我们将基于Tensorflow的Seq2Seq+Attention模型,介绍如何训练一个中文的自动生成新闻标题的模型。自动总结(Automatic Summarization)类型的模型一直是研究热点。 直接抽出重要的句子的抽取式方法较为简单,有如textrank之类的算法,而生成式(重新生成新句子)较为复杂,效果也不尽如人意。目前比较流行的Seq2Seq模型,由 Sutskever等人提出,基于一个Encoder-Decoder的结构将source句子先Encode成一个固定维度d的向量,然后通过Decoder部分一个字符一个字符生成Target句子。添加入了Attention注意力分配机制后,使得Decoder在生成新的Target Sequence时,能得到之前Encoder编码阶段每个字符的隐藏层的信息向量Hidden State,使得生成新序列的准确度提高。

数据准备和预处理

我们选择公开的“搜狐新闻数据(SogouCS)”的语料,包含2012年6月—7月期间的新闻数据,超过1M的语料数据,包含新闻标题和正文的信息。数据集可以从搜狗lab下载。http://www.sogou.com/labs/resource/cs.php

数据的预处理阶段极为重要,因为在Encoder编码阶段处理那些信息,直接影响到整个模型的效果。我们主要对下列信息进行替换和处理:

  • 特殊字符:去除特殊字符,如:“「,」,¥,…”;
  • 括号内的内容:如表情符,【嘻嘻】,【哈哈】
  • 日期:替换日期标签为TAG_DATE,如:***年*月*日,****年*月,等等
  • 超链接URL:替换为标签TAG_URL;
  • 删除全角的英文:替换为标签TAG_NAME_EN;
  • 替换数字:TAG_NUMBER;

在对文本进行了预处理后,准备训练语料: 我们的Source序列,是新闻的正文,待预测的Target序列是新闻的标题。 我们截取正文的分词个数到MAX_LENGTH_ENC=120个词,是为了训练的效果正文部分不宜过长。标题部分截取到MIN_LENGTH_ENC = 30,即生成标题不超过30个词。

在data_util.py类中,生成训练数据时做了下列事情:

  • create_vocabulary()方法创建词典;
  • data_to_token_ids()方法把训练数据(content-train.txt)转化为对应的词ID的表示;

两个文件的格式:

[python] view plain copy
  1. # 数据1 正文 content-train.txt  
  2. 世间 本 没有 歧视 TAG_NAME_EN 歧视 源自于 人 的 内心 活动 TAG_NAME_EN “ 以爱 之 名 ” TAG_DATE 中国 艾滋病 反歧视 主题 创意 大赛 开幕 TAG_NAME_EN 让 “ 爱 ” 在 高校 流动 。 TAG_NAME_EN 详细 TAG_NAME_EN   
  3. 济慈 之 家 小朋友 感受 爱心 椅子  TAG_DATE TAG_NAME_EN 思源 焦点 公益 基金 向 盲童 孤儿院 “ 济慈 之 家 ” 提供 了 首 笔 物资 捐赠 。 这 笔 价值 近 万 元 的 物资 为 曲 美 家具 向 思源 · 焦点 公益 基金 提供 的 儿童 休闲椅 TAG_NAME_EN 将 用于 济慈 之 家 的 小孩子们 日常 使用 。   
  4. ...  

[python] view plain copy
  1. # 数据2 标题 title-train.txt  
  2. 艾滋病 反歧视 创意 大赛   
  3. 思源 焦点 公益 基金 联手 曲 美 家具 共 献 爱心   
  4. ...  

训练模型


[python] view plain copy
  1. #代码1  
  2. python headline.py  

预测

      运行predict.py, 交互地输入分好词的文本, 得到textsum的结果

[python] view plain copy
  1. #代码2-1  
  2. python predict.py  
  3.    
  4. # 输入和输出  
  5. #> 中央 气象台 TAG_DATE TAG_NUMBER 时 继续 发布 暴雨 蓝色 预警 TAG_NAME_EN 预计 TAG_DATE TAG_NUMBER 时至 TAG_DATE TAG_NUMBER 时 TAG_NAME_EN 内蒙古 东北部 、 山西 中 北部 、 河北 中部 和 东北部 、 京津 地区 、 辽宁 西南部 、 吉林 中部 、 黑龙江 中部 偏南 等 地 的 部分 地区 有 大雨 或 暴雨 。  
  6. #current bucket id0  
  7. #中央 气象台 发布 暴雨 蓝色 预警  
  8. #>  

我们尝试输入下列分好词的新闻正文,一些挑选过的自动生成的中文标题如下:

ID新闻正文新闻标题textsum自动生成标题
469中央 气象台 TAG_DATE TAG_NUMBER 时 继续 发布 暴雨 蓝色 预警 TAG_NAME_EN 预计 TAG_DATE TAG_NUMBER 时至 TAG_DATE TAG_NUMBER 时 TAG_NAME_EN 内蒙古 东北部 、 山西 中 北部 、 河北 中部 和 东北部 、 京津 地区 、 辽宁 西南部 、 吉林 中部 、 黑龙江 中部 偏南 等 地 的 部分 地区 有 大雨 或 暴雨 。中央 气象台 继续 发布 暴雨 预警 北京 等 地 有 大雨中央 气象台 发布 暴雨 蓝色 预警
552美国 科罗拉多州 山林 大火 持续 肆虐 TAG_NAME_EN 当地 时间 TAG_DATE 横扫 州 内 第二 大 城市 科罗拉多斯 普林斯 一 处 居民区 TAG_NAME_EN 迫使 超过 TAG_NUMBER TAG_NAME_EN TAG_NUMBER 万 人 紧急 撤离 。 美国 正 值 山火 多发 季 TAG_NAME_EN 现有 TAG_NUMBER 场 山火 处于 活跃 状态 。山火 横扫 美 西部 TAG_NUMBER 州 奥 巴马 将 赴 灾区 视察 联邦 调查局 介入 查 原因美国 科罗拉多州 山火 致 TAG_NUMBER 人 死亡
917埃及 选举 委员会 昨天 宣布 TAG_NAME_EN 穆斯林 兄弟会 下属 自由 与 正义党 主席 穆尔西 获得 TAG_NUMBER TAG_NAME_EN TAG_NUMBER TAG_NAME_EN 的 选票 TAG_NAME_EN 以 微弱 优势 击败 前 总理 沙 菲克 赢得 选举 TAG_NAME_EN 成为 新任 埃及 总统 。 媒体 称 其 理念 获 下层 民众 支持 。埃及 大选 昨晚 结束 新 总统 穆尔西 被 认为 具有 改革 魄力埃及 总统 选举 结果
920上 周 TAG_NAME_EN 广东 华兴 银行 在 央行 宣布 降息 和 调整 存贷款 波幅 的 第二 天 TAG_NAME_EN 立即 宣布 首 套 房贷 利率 最低 执行 七 折 优惠 。 一 石 激起 千层 浪 TAG_NAME_EN 随之 而 起 的 “ 房贷 七 折 利率 重 出 江湖 ” 和 “ 房地产 调控 松绑 ” 的 谣言 四起 。房贷 “ 七 折 利率 ” 真相 调查 TAG_NAME_EN 符合 条件 的 几乎 为零银监会 否认 房贷 房贷 利率

预测并计算ROUGE评估

运行predict.py, 同时调用eval.py 中的 evaluate(X, Y, method = "rouge_n", n = 2) 方法计算ROUGE分


[python] view plain copy
  1. #代码2-2 linux shell  
  2. folder_path=`pwd`  
  3. input_dir=${folder_path}/news/test/content-test.txt  
  4. reference_dir=${folder_path}/news/test/title-test.txt  
  5. summary_dir=${folder_path}/news/test/summary.txt  
  6.    
  7. python predict.py $input_dir $reference_dir $summary_dir  
  8.    
  9. # 输出:  
  10. # 中央 气象台 发布 暴雨 蓝色 预警  
  11. # Evaludated Rouge-2 score is 0.1818  
  12. # ...  


下面我们将具体介绍tensorflow的seq2seq模型如何实现,首先先简单回顾模型的结构。

Seq2Seq+Attention模型回顾

Seq2Seq模型有效地建模了基于输入序列,预测未知输出序列的问题。模型有两部分构成,一个编码阶段的”Encoder”和一个解码阶段的”Decoder”。如下图的简单结构所示,Encoder的RNN每次输入一个字符代表的embedding向量,如依次输入A,B,C, 及终止标志,将输入序列编码成一个固定长度的向量;之后解码阶段的RNN会一个一个字符地解码, 如预测为X, 之后在训练阶段会强制将前一步解码的输出作为下一步解码的输入,如X会作为下一步预测Y时的输入。


图1 Seq2Seq model

定义输入序列 ,由Tx个固定长度为d的向量构成; 输出序列为 ,由Ty个固定长度为d的向量构成; 定义输入序Encoder阶段的RNN隐藏层为 hj, Decoder阶段的RNN隐藏层为 Si

Attention注意力分配机制

LSTM模型虽然具有记忆性,但是当Encoder阶段输入序列过长时,解码阶段的LSTM也无法很好地针对最早的输入序列解码。基于此,Attention注意力分配的机制被提出,就是为了解决这个问题。在Decoder阶段每一步解码,都能够有一个输入,对输入序列所有隐藏层的信息h_1,h_2,…h_Tx进行加权求和。打个比方就是每次在预测下一个词时都会把所有输入序列的隐藏层信息都看一遍,决定预测当前词时和输入序列的那些词最相关。

Attention机制代表了在解码Decoder阶段,每次都会输入一个Context上下文的向量Ci, 隐藏层的新状态Si根据上一步的状态Si-1, Yi, Ci 三者的一个非线性函数得出。



Context向量在解码的每一步都会重新计算,根据一个MLP模型计算出输出序列i对每个输入序列j的隐含层的对应权重aij,并对所有隐含层加权平均。文章中说的Alignment Model就是代表这种把输入序列位置j和输出序列位置i建立关系的模型。



aij 即可以理解为Decoder解码输出序列的第i步,对输入序列第j步分配的注意力权重。





eij为一个简单的MLP模型激活的输出;aij的计算是对eij做softmax归一化后的结果。


图2 Translate Seq2Seq alignment model

Soft Attention和Hard Attention区别

Soft Attention通常是指以上我们描述的这种全连接(如MLP计算Attention 权重),对每一层都可以计算梯度和后向传播的模型;不同于Soft attention那样每一步都对输入序列的所有隐藏层hj(j=1….Tx) 计算权重再加权平均的方法,Hard Attention是一种随机过程,每次以一定概率抽样,以一定概率选择某一个隐藏层 hj*,在估计梯度时也采用蒙特卡罗抽样Monte Carlo sampling的方法。



图3 Soft Attention 模型



图4 Hard Attention 模型

模型实现

我们对Tensorflow基本教程里的translate英语法语翻译例子里的seq2seq_model.py类稍加修改,就能够符合我们textsum例子使用,另外我们还会分析针对英文的textsum教程中构建双向Bi-LSTM的Encoder-Decoder的例子。

1.Seq2Seq模型文件: seq2Seq_model.py

单向LSTM的Encoder-Decoder结构

教程中的例子很长,但是将实例代码分解来看不是那么复杂,下面将分三段来介绍官方tutorial里的如何构建seq2seq模型。

定义基本单元: 多层LSTM cell

[python] view plain copy
  1. #代码3-1  
  2. # Create the internal multi-layer cell for our RNN.  
  3. single_cell = tf.nn.rnn_cell.GRUCell(size) # default use GRU  
  4. if use_lstm:  
  5.   single_cell = tf.nn.rnn_cell.BasicLSTMCell(size, state_is_tuple=True)  
  6. cell = single_cell  
  7. if num_layers > 1:  
  8.   cell = tf.nn.rnn_cell.MultiRNNCell([single_cell] * num_layers, state_is_tuple=True)  

定义前向过程的 seq2seq_f 函数,利用tf.nn.seq2seq.embedding_attention_seq2seq 返回output

[python] view plain copy
  1. # 代码3-2  
  2. # The seq2seq function: we use embedding for the input and attention.  
  3. def seq2seq_f(encoder_inputs, decoder_inputs, do_decode):  
  4.   return tf.nn.seq2seq.embedding_attention_seq2seq(  
  5.       encoder_inputs,  
  6.       decoder_inputs,  
  7.       cell,  
  8.       num_encoder_symbols=source_vocab_size,  
  9.       num_decoder_symbols=target_vocab_size,  
  10.       embedding_size=size,  
  11.       output_projection=output_projection,  
  12.       feed_previous=do_decode,  
  13.       dtype=tf.float32)  

桶Bucket的机制: 应用Bucket机制,核心的思想是把输入序列的句子按照长度的相似程度分到不同的固定长度的Bucket里面,长度不够的都添加PAD字符。之所以有Bucket的原因是工程效率:”RNN在数学上是可以处理任意长度的数据的。我们在TensorFlow中使用bucket的原因主要是为了工程实现的效率” (摘自知乎JQY的回答https://www.zhihu.com/question/42057513)


[python] view plain copy
  1. # 代码3-3  
  2. # Training outputs and losses.  
  3. if forward_only:  
  4.   self.outputs, self.losses = tf.nn.seq2seq.model_with_buckets(  
  5.       self.encoder_inputs, self.decoder_inputs, targets,  
  6.       self.target_weights, buckets, lambda x, y: seq2seq_f(x, y, True),  
  7.       softmax_loss_function=softmax_loss_function)  
  8.   # If we use output projection, we need to project outputs for decoding.  
  9.   if output_projection is not None:  
  10.     for b in xrange(len(buckets)):  
  11.       self.outputs[b] = [  
  12.           tf.matmul(output, output_projection[0]) + output_projection[1]  
  13.           for output in self.outputs[b]  
  14.       ]  
  15. else:  
  16.   self.outputs, self.losses = tf.nn.seq2seq.model_with_buckets(  
  17.       self.encoder_inputs, self.decoder_inputs, targets,  
  18.       self.target_weights, buckets, lambda x, y: seq2seq_f(x, y, False),  
  19.       softmax_loss_function=softmax_loss_function)  

tf.nn.seq2seq.model_with_buckets()结果返回output序列;’ buckets = [(120, 30), (200, 35), (300, 40), (400, 40), (500, 40)]

https://github.com/tensorflow/tensorflow/blob/64edd34ce69b4a8033af5d217cb8894105297d8a/tensorflow/contrib/legacy_seq2seq/python/ops/seq2seq.py

双向LSTM的Encoder-Decoder结构

Encoder阶段自己定义了Bi-LSTM结构

[python] view plain copy
  1. # 代码4-1  
  2. # URL: https://github.com/tensorflow/models/tree/master/textsum  
  3. # Encoder: Multi-Layer LSTM, Output: encoder_outputs  
  4.   for layer_i in xrange(hps.enc_layers):  
  5.     with tf.variable_scope('encoder%d'%layer_i), tf.device(  
  6.         self._next_device()):  
  7.       cell_fw = tf.nn.rnn_cell.LSTMCell(  
  8.           hps.num_hidden,  
  9.           initializer=tf.random_uniform_initializer(-0.10.1, seed=123),  
  10.           state_is_tuple=False)  
  11.       cell_bw = tf.nn.rnn_cell.LSTMCell(  
  12.           hps.num_hidden,  
  13.           initializer=tf.random_uniform_initializer(-0.10.1, seed=113),  
  14.           state_is_tuple=False)  
  15.       (emb_encoder_inputs, fw_state, _) = tf.nn.bidirectional_rnn(  
  16.           cell_fw, cell_bw, emb_encoder_inputs, dtype=tf.float32,  
  17.           sequence_length=article_lens)  
  18.   encoder_outputs = emb_encoder_inputs  
  19.    
  20.   with tf.variable_scope('output_projection'):  
  21.     w = tf.get_variable(  
  22.         'w', [hps.num_hidden, vsize], dtype=tf.float32,  
  23.         initializer=tf.truncated_normal_initializer(stddev=1e-4))  
  24.     w_t = tf.transpose(w)  
  25.     v = tf.get_variable(  
  26.         'v', [vsize], dtype=tf.float32,  
  27.         initializer=tf.truncated_normal_initializer(stddev=1e-4))  
  28. Decoder阶段,利用内置的tf.nn.seq2seq.attention_decoder()函数返回output  

[python] view plain copy
  1. # 代码4-2  
  2.   with tf.variable_scope('decoder'), tf.device(self._next_device()):  
  3.     # When decoding, use model output from the previous step  
  4.     # for the next step.  
  5.     loop_function = None  
  6.     if hps.mode == 'decode':  
  7.       loop_function = _extract_argmax_and_embed(  
  8.           embedding, (w, v), update_embedding=False)  
  9.    
  10.     cell = tf.nn.rnn_cell.LSTMCell(  
  11.         hps.num_hidden,  
  12.         initializer=tf.random_uniform_initializer(-0.10.1, seed=113),  
  13.         state_is_tuple=False)  
  14.    
  15.     encoder_outputs = [tf.reshape(x, [hps.batch_size, 12*hps.num_hidden])  
  16.                        for x in encoder_outputs]  
  17.     self._enc_top_states = tf.concat(1, encoder_outputs)  
  18.     self._dec_in_state = fw_state  
  19.     # During decoding, follow up _dec_in_state are fed from beam_search.  
  20.     # dec_out_state are stored by beam_search for next step feeding.  
  21.     initial_state_attention = (hps.mode == 'decode')  
  22.     decoder_outputs, self._dec_out_state = tf.nn.seq2seq.attention_decoder(  
  23.         emb_decoder_inputs, self._dec_in_state, self._enc_top_states,  
  24.         cell, num_heads=1, loop_function=loop_function,  
  25.         initial_state_attention=initial_state_attention)  

Attention注意力矩阵的可视化

Attention 注意力分配机制的权重矩阵[Aij]可以反映在Decoder阶段第i个输出字符对Encoder阶段的第j个字符的注意力分配的权重aij。 我们可以通过绘制Heatmap来可视化seq2seq模型中Decoder的Y对Encoder的X每个字符的权重。

获取attention_mask的值

attention_mask 即为我们感兴趣的注意力权重分配的tensor,我们首先来看tensorflow的源码seq2seq.py这个ops的实现, 容易发现,计算attention_mask 变量a的代码出现在 attention_decoder()函数内的attention()函数体下, a = nn_ops.softmax(s) 这句。 我们把该变量添加到return语句中的返回值,同时也修改所有调用了attention_decoder()的上层的函数,为了最终能够在主函数中将attn_mask这个变量抽取出。 具体需要修改的脚本参考textsum项目下的seq2seq_attn.py这个文件。 之后我们在主函数中利用attn_out = session.run(self.attn_masks[bucket_id], input_feed) ,对变量进行session.run() 就可以获得当前这个样本的attention矩阵的值。

[python] view plain copy
  1. # 代码5-1  
  2. # URL: https://github.com/rockingdingo/deepnlp/blob/master/deepnlp/textsum/seq2seq_attn.py  
  3.    
  4. def attention_decoder():  
  5.     ## some code  
  6.        
  7.     def attention(query):  
  8.       """Put attention masks on hidden using hidden_features and query."""  
  9.       ds = []  
  10.       if nest.is_sequence(query):  
  11.         query_list = nest.flatten(query)  
  12.         for q in query_list:  
  13.           ndims = q.get_shape().ndims  
  14.           if ndims:  
  15.             assert ndims == 2  
  16.         query = array_ops.concat_v2(query_list, 1)  
  17.       for a in xrange(num_heads):  
  18.         with variable_scope.variable_scope("Attention_%d" % a):  
  19.           y = linear(query, attention_vec_size, True)  
  20.           y = array_ops.reshape(y, [-111, attention_vec_size])  
  21.           # Attention mask is a softmax of v^T * tanh(...).  
  22.           s = math_ops.reduce_sum(v[a] * math_ops.tanh(hidden_features[a] + y),  
  23.                                   [23])  
  24.              
  25.           # Tensor a 即为我们需要抽取的attention_mask  
  26.           a = nn_ops.softmax(s)  
  27.              
  28.           d = math_ops.reduce_sum(  
  29.               array_ops.reshape(a, [-1, attn_length, 11]) * hidden, [12])  
  30.           ds.append(array_ops.reshape(d, [-1, attn_size]))  
  31.       return ds, a  

利用matplotlib可视化

我们利用matplotlib包中绘制heatmap的函数,可以简单地将上一步抽取出的attn_matrix可视化。在eval.py模块中我们整合了一个eval.plot_attention(data, X_label=None, Y_label=None) 函数来简单绘制attention权重矩阵。 运行 predict_attn.py 脚本,输入分好词的待分析新闻文本,然后自动生成的jpg图片就保存在./img目录下。


[python] view plain copy
  1. # 代码5-2  
  2. # 输入文本, 查看Attention的heatmap:  
  3. # > 中央 气象台 TAG_DATE TAG_NUMBER 时 继续 发布 暴雨 蓝色 预警 TAG_NAME_EN 预计 TAG_DATE TAG_NUMBER 时至 TAG_DATE TAG_NUMBER 时 TAG_NAME_EN 内蒙古 东北部 、 山西 中 北部 、 河北 中部 和 东北部 、 京津 地区 、 辽宁 西南部 、 吉林 中部 、 黑龙江 中部 偏南 等 地 的 部分 地区 有 大雨 或 暴雨 。  
  4.    
  5. python predict_attn.py  





图5 Attention注意力权重分配的Heatmap

可视化部分代码参考项目中的 predict_attn.py seq2seq_attn.py seq2seq_model_attn.py 这三个文件。

预测阶段Decode策略: 贪心算法和Beam Search

贪心算法 Beam Search

拓展阅读

1
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

seq2seq学习笔记

@author: huangyongye1. RNN基础对于RNN,我看到讲得最通俗易懂的应该是Andrej发的博客: The Unreasonable Effectiveness of Recur...
  • Jerr__y
  • Jerr__y
  • 2016-12-20 11:41
  • 29737

seq2seq模型

seq2seq模型,简单点说,是一个翻译模型,把一个sequence翻译成另一个sequence,最早在SMT领域被证明。其基本思想是两个RNN LM,一个作为encoder,另一个作为decoder...
  • sunlylorn
  • sunlylorn
  • 2016-01-29 15:08
  • 19409

模型汇总16 各类Seq2Seq模型对比及《Attention Is All You Need》中技术详解

1、已有Seq2Seq模型 Seq2Seq模型是处理序列到序列问题的利器,尤其是在神经网络翻译(NMT)方面,取得了很大的成功。Seq2Seq由一个encoder和一个decoder构成,encode...
  • lqfarmer
  • lqfarmer
  • 2017-06-20 22:00
  • 1857

用tensorflow实现seq2seq模型

训练数据和预处理数据集是电影剧本中的对话,我们首先需要做一些预处理以获得正确的数据格式。 切字分词 使用结巴分词。 移除低频词 代码中,用vocabulary_size 限制词表的大小。用UN...
  • u013713117
  • u013713117
  • 2017-02-11 20:37
  • 7607

Seq2Seq模型

前言: 此文翻译自TensorFlow tutorial: Sequence-to-Sequence Models 阅读完之后感觉挺好的,所以萌生了翻译的念头。 本文的尽量在做到意思正确的情况下...
  • wuzqChom
  • wuzqChom
  • 2017-08-03 21:55
  • 1816

深度学习的seq2seq模型

深度学习的seq2seq模型 原创 2017年09月07日 16:21:22 829 0 1 从rnn结构说起 根据输出和输入序...
  • starzhou
  • starzhou
  • 2017-10-07 21:48
  • 249

Seq2Seq非常好的代码(机器翻译、对话生成等):漫谈四种神经网络序列解码模型【附示例代码】

感谢作者,写的非常好 http://jacoxu.com/encoder_decoder/(博客原文) https://github.com/jacoxu/encoder_decode...
  • mmc2015
  • mmc2015
  • 2017-05-26 21:49
  • 3889

Tensorflow 自动文摘: 基于Seq2Seq+Attention模型的Textsum模型

这篇文章中我们将基于Tensorflow的Seq2Seq+Attention模型,介绍如何训练一个中文的自动生成新闻标题的模型。自动总结(Automatic Summarization)类型的模型一直...
  • rockingdingo
  • rockingdingo
  • 2017-02-15 21:42
  • 14892

tensorflow seq2seq模型 代码阅读分析

如果刚开始入门该模型请阅读tf官方说明:Sequence-to-Sequence Models模型应用于机器翻译的示例代码:github如果还没有看懂tf的translate示例代码,请先理解透彻tr...
  • vincent_hbl
  • vincent_hbl
  • 2017-08-11 15:08
  • 1098
    个人资料
    • 访问:1792562次
    • 积分:20735
    • 等级:
    • 排名:第447名
    • 原创:102篇
    • 转载:1377篇
    • 译文:6篇
    • 评论:253条
    联系方式
    个人邮箱: xuxiduo@zju.edu.cn
    QQ群:
    1)OpenCV俱乐部
        186168905

    2) 视频/音频/图像/算法/ML
        群1:148111910

        群2:157103105

    备注:加群需要回答问题,避免广告党。
    如果你是博客看到后加的,请注明“博客”并回答问题,只注明”博客“不回答问题的恕不加入。答案为和群相关的任何技术名词,不能出现1)和2)中的任何字眼
    博客专栏
    文章分类
    最新评论