2018年10月11日笔记

tensorflow是谷歌google的深度学习框架,tensor中文叫做张量,flow叫做流。
RNN是recurrent neural network的简称,中文叫做循环神经网络。
文本分类是NLP(自然语言处理)的经典任务。

0.编程环境

操作系统:Win10
python版本:3.6
集成开发环境:jupyter notebook
tensorflow版本:1.6

1.致谢声明

本文是作者学习《使用卷积神经网络以及循环神经网络进行中文文本分类》的成果,感激前辈;
github链接:https://github.com/gaussic/text-classification-cnn-rnn

2.配置环境

使用循环神经网络模型要求有较高的机器配置,如果使用CPU版tensorflow会花费大量时间。
读者在有nvidia显卡的情况下,安装GPU版tensorflow会提高计算速度。
安装教程链接:https://blog.csdn.net/qq_36556893/article/details/79433298
如果没有nvidia显卡,但有visa信用卡,请阅读我的另一篇文章《在谷歌云服务器上搭建深度学习平台》,链接:https://www.jianshu.com/p/893d622d1b5a

3.下载并解压数据集

数据集下载链接: https://pan.baidu.com/s/1oLZZF4AHT5X_bzNl2aF2aQ 提取码: 5sea
下载压缩文件cnews.zip完成后,选择解压到cnews,如下图所示:

10345471-b66ecd374b9e5bde.png

image.png

文件夹结构如下图所示:

10345471-1734e69438d3375b.png

image.png

cnew文件夹中有4个文件:
1.训练集文件cnews.train.txt
2.测试集文件cnew.test.txt
3.验证集文件cnews.val.txt
4.词汇表文件cnews.vocab.txt
共有10个类别,65000个样本数据,其中训练集50000条,测试集10000条,验证集5000条。

4.完整代码

代码文件需要放到和cnews文件夹同级目录
给读者提供完整代码,旨在读者能够直接运行代码,有直观的感性认识。
如果要理解其中代码的细节,请阅读后面的章节

import warnings
warnings.filterwarnings('ignore')
import time
startTime = time.time()
def printUsedTime():used_time = time.time() - startTimeprint('used time: %.2f seconds' %used_time)
with open('./cnews/cnews.train.txt', encoding='utf8') as file:line_list = [k.strip() for k in file.readlines()]train_label_list = [k.split()[0] for k in line_list]train_content_list = [k.split(maxsplit=1)[1] for k in line_list]
with open('./cnews/cnews.vocab.txt', encoding='utf8') as file:vocabulary_list = [k.strip() for k in file.readlines()]
print('0.load train data finished')
printUsedTime()
word2id_dict = dict([(b, a) for a, b in enumerate(vocabulary_list)])
content2idList = lambda content : [word2id_dict[word] for word in content if word in word2id_dict]
train_idlist_list = [content2idList(content) for content in train_content_list]
vocabolary_size = 5000  # 词汇表达小
sequence_length = 150  # 序列长度
embedding_size = 64  # 词向量大小
num_hidden_units = 256  # LSTM细胞隐藏层大小
num_fc1_units = 64 #第1个全连接下一层的大小
dropout_keep_probability = 0.5  # dropout保留比例
num_classes = 10  # 类别数量
learning_rate = 1e-3  # 学习率
batch_size = 64  # 每批训练大小
import tensorflow.contrib.keras as kr
train_X = kr.preprocessing.sequence.pad_sequences(train_idlist_list, sequence_length)
from sklearn.preprocessing import LabelEncoder
labelEncoder = LabelEncoder()
train_y = labelEncoder.fit_transform(train_label_list)
train_Y = kr.utils.to_categorical(train_y, num_classes)
import tensorflow as tf
tf.reset_default_graph()
X_holder = tf.placeholder(tf.int32, [None, sequence_length])
Y_holder = tf.placeholder(tf.float32, [None, num_classes])
print('1.data preparation finished')
printUsedTime()embedding = tf.get_variable('embedding', [vocabolary_size, embedding_size])
embedding_inputs = tf.nn.embedding_lookup(embedding, X_holder)
gru_cell = tf.contrib.rnn.GRUCell(num_hidden_units)
outputs, state = tf.nn.dynamic_rnn(gru_cell,embedding_inputs, dtype=tf.float32)
last_cell = outputs[:, -1, :]
full_connect1 = tf.layers.dense(last_cell,num_fc1_units)
full_connect1_dropout = tf.contrib.layers.dropout(full_connect1,dropout_keep_probability)
full_connect1_activate = tf.nn.relu(full_connect1_dropout)
full_connect2 = tf.layers.dense(full_connect1_activate,num_classes)
predict_Y = tf.nn.softmax(full_connect2)
cross_entropy = tf.nn.softmax_cross_entropy_with_logits_v2(labels=Y_holder,logits=full_connect2)
loss = tf.reduce_mean(cross_entropy)
optimizer = tf.train.AdamOptimizer(learning_rate)
train = optimizer.minimize(loss)
isCorrect = tf.equal(tf.argmax(Y_holder,1), tf.argmax(predict_Y, 1))
accuracy = tf.reduce_mean(tf.cast(isCorrect, tf.float32))
print('2.build model finished')
printUsedTime()init = tf.global_variables_initializer()
session = tf.Session()
session.run(init)
print('3.initialize variable finished')
printUsedTime()with open('./cnews/cnews.test.txt', encoding='utf8') as file:line_list = [k.strip() for k in file.readlines()]test_label_list = [k.split()[0] for k in line_list]test_content_list = [k.split(maxsplit=1)[1] for k in line_list]
test_idlist_list = [content2idList(content) for content in test_content_list]
test_X = kr.preprocessing.sequence.pad_sequences(test_idlist_list, sequence_length)
test_y = labelEncoder.transform(test_label_list)
test_Y = kr.utils.to_categorical(test_y, num_classes)
print('4.load test data finished')
printUsedTime()
print('5.begin model training')
import random
for i in range(5000):selected_index = random.sample(list(range(len(train_y))), k=batch_size)batch_X = train_X[selected_index]batch_Y = train_Y[selected_index]session.run(train, {X_holder:batch_X, Y_holder:batch_Y})step = i + 1 if step % 100 == 0:selected_index = random.sample(list(range(len(test_y))), k=200)batch_X = test_X[selected_index]batch_Y = test_Y[selected_index]loss_value, accuracy_value = session.run([loss, accuracy], {X_holder:batch_X, Y_holder:batch_Y})print('step:%d loss:%.4f accuracy:%.4f' %(step, loss_value, accuracy_value))printUsedTime()

上面一段代码的运行结果如下(只截取前1000次迭代):

0.load train data finished
used time: 1.17 seconds
1.data preparation finished
used time: 15.78 seconds
2.building model finished
used time: 16.90 seconds
3.initialize variable finished
used time: 17.00 seconds
4.load test data finished
used time: 20.29 seconds
5.begin model training
step:100 loss:1.6444 accuracy:0.3800
used time: 41.93 seconds
step:200 loss:1.1358 accuracy:0.5750
used time: 64.20 seconds
step:300 loss:1.1324 accuracy:0.5950
used time: 86.21 seconds
step:400 loss:0.8088 accuracy:0.7300
used time: 108.22 seconds
step:500 loss:0.6240 accuracy:0.8200
used time: 130.22 seconds
step:600 loss:0.6347 accuracy:0.8000
used time: 152.16 seconds
step:700 loss:0.7305 accuracy:0.7900
used time: 174.48 seconds
step:800 loss:0.5374 accuracy:0.8650
used time: 196.56 seconds
step:900 loss:0.5020 accuracy:0.8650
used time: 218.81 seconds
step:1000 loss:0.5872 accuracy:0.8150
used time: 241.07 seconds

5.数据准备

读者阅读下文中的行数时,可以先把代码复制到jupyter notebook的代码块中。
在代码块中按Esc键,进入命令模式,代码块左边的竖线会显示蓝色,如下图所示:

10345471-5f9ae569750b1eb1.png

image.png

在命令模式下,点击L键,会显示代码行数。
本文作者解释每行代码含义如下:
第1行代码导入warnings库;
第2行代码中ignore中文叫做忽略,即不打印警告信息;
第3行代码导入time库;
第4行代码把程序开始时间赋值给变量startTime;
第5-7行代码定义printUsedTime函数,作用是打印程序运行时间;
第8行代码调用open方法打开文本文件
第9行代码使用列表推导式得到文本文件中的行内容列表赋值给变量label_list;
第10行代码得到训练集的标签列表赋值给变量train_label_list;
第11行代码得到训练集的内容列表赋值给变量train_content_list。
第12-13行代码得到词汇表文件cnews.vocab.txt中的词汇列表赋值给变量vocabulary_list;
第14行代码打印提示信息0.load train data finished,表示加载训练集数据完成;
第15行代码打印程序运行至此步使用的时间
第16行代码使用列表推导式得到词汇及其id对应的列表,并调用dict方法将列表强制转换为字典。
打印变量word2id_dict的前5项,如下图所示:

10345471-25caf95abfd59016.png

image.png

第17行代码使用列表推导式和匿名函数定义函数content2idlist,函数作用是将文章中的每个字转换为id;
第18行代码使用列表推导式得到的结果是列表的列表,总列表train_idlist_list中的元素是每篇文章中的字对应的id列表;
第19-27这9行代码设置循环神经网络的超参数;
第28-33这6行代码获得能够用于模型训练的特征矩阵预测目标值
第28行代码导入tensorflow.contrib.keras库,取别名kr;
第29行代码将每个样本统一长度为seq_length,即600;
第30行代码导入sklearn.preprocessing库的labelEncoder方法;
第31行代码实例化LabelEncoder对象;
第32行代码调用LabelEncoder对象的fit_transform方法做标签编码
第33行代码调用keras.untils库的to_categorical方法将标签编码的结果再做Ont-Hot编码
第34行代码导入tensorflow库,取别名tf;
第35行代码重置tensorflow图,加强代码的健壮性
第36-37行代码中placeholder中文叫做占位符,将每次训练的特征矩阵X预测目标值Y赋值给变量X_holder和Y_holder;
第38行代码打印提示信息1.data preparation finished,表示数据准备完成
第39行代码打印程序运行至此步使用的时间

import warnings
warnings.filterwarnings('ignore')
import time
startTime = time.time()
def printUsedTime():used_time = time.time() - startTimeprint('used time: %.2f seconds' %used_time)
with open('./cnews/cnews.train.txt', encoding='utf8') as file:line_list = [k.strip() for k in file.readlines()]train_label_list = [k.split()[0] for k in line_list]train_content_list = [k.split(maxsplit=1)[1] for k in line_list]
with open('./cnews/cnews.vocab.txt', encoding='utf8') as file:vocabulary_list = [k.strip() for k in file.readlines()]
print('0.load train data finished')
printUsedTime()
word2id_dict = dict([(b, a) for a, b in enumerate(vocabulary_list)])
content2idList = lambda content : [word2id_dict[word] for word in content if word in word2id_dict]
train_idlist_list = [content2idList(content) for content in train_content_list]
vocabolary_size = 5000  # 词汇表达小
sequence_length = 150  # 序列长度
embedding_size = 64  # 词向量大小
num_hidden_units = 256  # LSTM细胞隐藏层大小
num_fc1_units = 64 #第1个全连接下一层的大小
dropout_keep_probability = 0.5  # dropout保留比例
num_classes = 10  # 类别数量
learning_rate = 1e-3  # 学习率
batch_size = num_classes * 5   # 每批训练大小
import tensorflow.contrib.keras as kr
train_X = kr.preprocessing.sequence.pad_sequences(train_idlist_list, sequence_length)
from sklearn.preprocessing import LabelEncoder
labelEncoder = LabelEncoder()
train_y = labelEncoder.fit_transform(train_label_list)
train_Y = kr.utils.to_categorical(train_y, num_classes)
import tensorflow as tf
tf.reset_default_graph()
X_holder = tf.placeholder(tf.int32, [None, sequence_length])
Y_holder = tf.placeholder(tf.float32, [None, num_classes])
print('1.data preparation finished')
printUsedTime()

6.搭建神经网络

第1-2行代码调用tf库的get_variable方法实例化可以更新的模型参数embedding,矩阵形状为vocabulary_size*embedding_size,即5000*64
第3-4行代码调用tf.nn库的embedding_lookup方法将输入数据做词嵌入,得到新变量embedding_inputs的形状为batch_size*sequence_length*embedding_size,即50*100*64
理解word2vec原理,推荐阅读文章链接:https://www.jianshu.com/p/471d9bfbd72f
第5行代码调用tf.contrib.rnn.GRUCell方法实例化GRU细胞对象;
第6-8行代码调用tf.nn.dynamic_rnn方法动态计算循环神经网络中的结果,outputs是每个细胞的h的结果,state是最后一个细胞的h和c的结果,LSTM网络中h是短时记忆矩阵,c是长时记忆矩阵,想要理解c和h,请自行查找和学习LSTM理论;
第9行代码获取最后一个细胞的h,即最后一个细胞的短时记忆矩阵,等价于state.h
第10-11行代码添加全连接层,tf.layers.dense方法结果赋值给变量full_connect1,形状为batch_size*num_fc1_units,即50*128
第12-13行代码调用tf.contrib.layers.dropout方法,方法需要2个参数,第1个参数是输入数据,第2个参数是保留比例;
第14行代码调用tf.nn.relu方法,即激活函数;
第15-16行代码添加全连接层,tf.layers.dense方法结果赋值给变量full_connect2,形状为batch_size*num_classes,即50*10
第17行代码调用tf.nn.softmax方法,方法结果是预测概率值;
第18-20行代码使用交叉熵作为损失函数;
第21行代码调用tf.train.AdamOptimizer方法定义优化器optimizer;
第22行代码调用优化器对象的minimize方法,即最小化损失;
第23-24行代码计算预测准确率;
第25行代码打印提示信息2.build model finished,表示搭建神经网络完成;
第26行代码打印程序运行至此步使用的时间

embedding = tf.get_variable('embedding', [vocabolary_size, embedding_size])
embedding_inputs = tf.nn.embedding_lookup(embedding, X_holder)
gru_cell = tf.contrib.rnn.GRUCell(num_hidden_units)
outputs, state = tf.nn.dynamic_rnn(gru_cell,embedding_inputs, dtype=tf.float32)
last_cell = outputs[:, -1, :]
full_connect1 = tf.layers.dense(last_cell,num_fc1_units)
full_connect1_dropout = tf.contrib.layers.dropout(full_connect1,dropout_keep_probability)
full_connect1_activate = tf.nn.relu(full_connect1_dropout)
full_connect2 = tf.layers.dense(full_connect1_activate,num_classes)
predict_Y = tf.nn.softmax(full_connect2)
cross_entropy = tf.nn.softmax_cross_entropy_with_logits_v2(labels=Y_holder,logits=full_connect2)
loss = tf.reduce_mean(cross_entropy)
optimizer = tf.train.AdamOptimizer(learning_rate)
train = optimizer.minimize(loss)
isCorrect = tf.equal(tf.argmax(Y_holder,1), tf.argmax(predict_Y, 1))
accuracy = tf.reduce_mean(tf.cast(isCorrect, tf.float32))
print('2.build model finished')
printUsedTime()

7.参数初始化

对于神经网络模型,重要是其中的W、b这两个参数。
开始神经网络模型训练之前,这两个变量需要初始化。
第1行代码调用tf.global_variables_initializer实例化tensorflow中的Operation对象。

10345471-675b492bd6c8bcbe.png

image.png

第2行代码调用tf.Session方法实例化会话对象;
第3行代码调用tf.Session对象的run方法做变量初始化。
第4行代码打印提示信息3.initialize variable finished,表示参数初始化完成;
第5行代码打印程序运行至此步使用的时间

init = tf.global_variables_initializer()
session = tf.Session()
session.run(init)
print('3.initialize variable finished')
printUsedTime()

8.模型训练

第1-8行代码获取文本文件cnews.test.txt,即测试集中的数据;
第9行代码打印提示信息4.load test data finished,表示加载测试集数据完成;
第10行代码打印程序运行至此步使用的时间
第11行代码打印提示信息5.begin model training,表示开始模型训练;
第12行代码导入random库;
第13行表示模型迭代训练5000次;
第14-16行代码从训练集中选取batch_size大小,即50个样本做批量梯度下降
第17行代码每运行1次,表示模型训练1次;
第18行代码记录当前步数,赋值给变量step;
第19行代码表示每间隔100步打印;
第20-22行代码从测试集中随机选取200个样本;
第23行代码表示计算损失值loss_value、准确率accuracy_value;
第24行代码表示打印步数step、损失值loss_value、准确率accuracy_value;
第25行代码打印程序运行至此步使用的时间

with open('./cnews/cnews.test.txt', encoding='utf8') as file:line_list = [k.strip() for k in file.readlines()]test_label_list = [k.split()[0] for k in line_list]test_content_list = [k.split(maxsplit=1)[1] for k in line_list]
test_idlist_list = [content2idList(content) for content in test_content_list]
test_X = kr.preprocessing.sequence.pad_sequences(test_idlist_list, sequence_length)
test_y = labelEncoder.transform(test_label_list)
test_Y = kr.utils.to_categorical(test_y, num_classes)
print('4.load test data finished')
printUsedTime()
print('5.begin model training')
import random
for i in range(5000):selected_index = random.sample(list(range(len(train_y))), k=batch_size)batch_X = train_X[selected_index]batch_Y = train_Y[selected_index]session.run(train, {X_holder:batch_X, Y_holder:batch_Y})step = i + 1 if step % 100 == 0:selected_index = random.sample(list(range(len(test_y))), k=200)batch_X = test_X[selected_index]batch_Y = test_Y[selected_index]loss_value, accuracy_value = session.run([loss, accuracy], {X_holder:batch_X, Y_holder:batch_Y})print('step:%d loss:%.4f accuracy:%.4f' %(step, loss_value, accuracy_value))printUsedTime()

9.词汇表

经过前文5-8章的讲解,已经完成循环神经网络的训练。
本项目提供词汇表文件cnews.vocab.txt,但在实践中需要自己统计语料的词汇表。
下面代码可以由内容列表content_list产生词汇表:

from collections import Counterdef getVocabularyList(content_list, vocabulary_size):allContent_str = ''.join(content_list)counter = Counter(allContent_str)vocabulary_list = [k[0] for k in counter.most_common(vocabulary_size)]return vocabulary_listdef makeVocabularyFile(content_list, vocabulary_size):vocabulary_list = getVocabularyList(content_list, vocabulary_size)with open('vocabulary.txt', 'w', encoding='utf8') as file:for vocabulary in vocabulary_list:file.write(vocabulary + '\n')makeVocabularyFile(train_content_list, 5000)

本段代码产生的文件,与提供的词汇表文件cnews.vocab.txt稍有不同。
造成原因有2点:
1.词汇表文件的第1个字<PAD>是补全字,无实际含义,与kr.preprocessing.sequence.pad_sequences方法补全的0对应;
2.源代码作者使用了训练集、验证集、测试集作为总语料库,上面一段代码只使用了训练集作为语料库。

10.混淆矩阵

import numpy as np
import pandas as pd
from sklearn.metrics import confusion_matrixdef predictAll(test_X, batch_size=100):predict_value_list = []for i in range(0, len(test_X), batch_size):selected_X = test_X[i: i + batch_size]predict_value = session.run(predict_Y, {X_holder:selected_X})predict_value_list.extend(predict_value)return np.array(predict_value_list)Y = predictAll(test_X)
y = np.argmax(Y, axis=1)
predict_label_list = labelEncoder.inverse_transform(y)
pd.DataFrame(confusion_matrix(test_label_list, predict_label_list), columns=labelEncoder.classes_,index=labelEncoder.classes_ )

上面一段代码的运行结果如下图所示:

10345471-85f7c5daeb50fb54.png

image.png

从上图的结果可以看出,家居类新闻分类效果较差。

11.报告表

下面一段代码能够成功运行的前提是已经运行第10章代码

import numpy as np
from sklearn.metrics import precision_recall_fscore_supportdef eval_model(y_true, y_pred, labels):# 计算每个分类的Precision, Recall, f1, supportp, r, f1, s = precision_recall_fscore_support(y_true, y_pred)# 计算总体的平均Precision, Recall, f1, supporttot_p = np.average(p, weights=s)tot_r = np.average(r, weights=s)tot_f1 = np.average(f1, weights=s)tot_s = np.sum(s)res1 = pd.DataFrame({u'Label': labels,u'Precision': p,u'Recall': r,u'F1': f1,u'Support': s})res2 = pd.DataFrame({u'Label': ['总体'],u'Precision': [tot_p],u'Recall': [tot_r],u'F1': [tot_f1],u'Support': [tot_s]})res2.index = [999]res = pd.concat([res1, res2])return res[['Label', 'Precision', 'Recall', 'F1', 'Support']]eval_model(test_label_list, predict_label_list, labelEncoder.classes_)

上面一段代码的运行结果如下图所示:

10345471-a7c6b17ac4393864.png

image.png

12.总结

1.本文是作者第5个NLP小型项目,数据共有65000条。
2.分类模型的评估指标F1score为0.89左右,总体来说这个分类模型比CNN模型效果差,而且训练时间更久;
3.本文为了节省读者的实验时间,设置sequence_length为150,迭代5000次总共花费1123秒,即18分43秒;
4.如果设置sequence_length为300,迭代5000次总共花费时间2184秒,即36分24秒,评估指标F1score为0.9282,如下图所示:

10345471-1cd6d55466f7ffb5.png

image.png

基于tensorflow+RNN的新浪新闻文本分类相关推荐

  1. 基于tensorflow+CNN的新浪新闻文本分类

    2018年10月4日笔记 tensorflow是谷歌google的深度学习框架,tensor中文叫做张量,flow叫做流. CNN是convolutional neural network的简称,中文 ...

  2. 基于tensorflow、CNN、清华数据集THUCNews的新浪新闻文本分类

    2018年10月14日笔记 tensorflow是谷歌google的深度学习框架,tensor中文叫做张量,flow叫做流. CNN是convolutional neural network的简称,中 ...

  3. 基于Python的新浪新闻文本分类

    资源下载地址:https://download.csdn.net/download/sheziqiong/85736113 语料库构建 本项目的语料来源新浪新闻网,通过 spider.py 爬虫模块获 ...

  4. 大数据初体验——基于地图可视化分析新浪新闻评论地域分布特点

    **概述:**本文带领大家初次体验大数据处理的一些经过,从从灵感的迸发,信息的获取,数据的处理,到地图可视化,各方面都进行了抛砖引玉的论述,对于部分技术细节也做了些许探讨,希望能和大家一起捅破大数据处 ...

  5. 【文本分类】基于BERT预训练模型的灾害推文分类方法、基于BERT和RNN的新闻文本分类对比

    ·阅读摘要: 两篇论文,第一篇发表于<图学学报>,<图学学报>是核心期刊:第二篇发表于<北京印刷学院学报>,<北京印刷学院学报>没有任何标签. ·参考文 ...

  6. 多线程新浪新闻搜索网络爬虫-基于关键字

    本文为原创博客,仅供技术学习使用.未经允许,禁止将其复制下来上传到百度文库等平台.如有转载请注明本文博客的地址(链接) 源码获取请联系:1563178220@qq.com 简介 互联网有海量的新闻,如 ...

  7. 新浪新闻评论系统的架构演进和经验总结

    评论系统,或者称为跟帖.留言板,是所有门户网站的核心标准服务组件之一.与论坛.博客等其他互联网UGC系统相比,评论系统虽然从产品功能角度衡量相对简单,但因为需要能够在突发热点新闻事件时,在没有任何预警 ...

  8. 基于朴素贝叶斯和LSTM的两种新闻文本分类方法

    新闻文本分类 文章目录 新闻文本分类 一.项目背景 二.数据处理与分析 三.基于机器学习的文本分类--朴素贝叶斯 1. 模型介绍 2. 代码结构 3. 结果分析 四.基于深度学习的文本分类--LSTM ...

  9. 门户级UGC系统的技术进化路线——新浪新闻评论系统的架构演进和经验总结

    评论系统,或者称为跟帖.留言板,是所有门户网站的核心标准服务组件之一.与论坛.博客等其他互联网UGC系统相比,评论系统虽然从产品功能角度衡量相对简单,但因为需要能够在突发热点新闻事件时,在没有任何预警 ...

最新文章

  1. 人工智能及其应用(第5版).蔡自兴-3章课后习题。【参考答案】
  2. Nginx 配置中一个不起眼字符 “/“ 的巨大作用
  3. mysql创建部分索引
  4. ZOJ 3171 The Hidden 7's DP
  5. Java 装饰器模式详解
  6. javaone_JavaOne 2012:掌握Java部署
  7. 使用了BeanUtils的简单操作
  8. linux打印mysql堆栈_第25问:MySQL 崩溃了,打印了一些堆栈信息,怎么读?
  9. 使用Python操作PowerPoint文件中的表格
  10. 形式语义学关于带权有向图最短路径的有模式函数算法
  11. 【笔记】程序性能优化28个策略原则
  12. html5光线传感器,光线传感器是什么 光线传感器分类
  13. ant pro-table点击表格行高亮显示
  14. 天龙八部雁门关外的故事
  15. 全志H3停产,A40I/T3更胜一筹--CoM-X40I核心模块来了
  16. 【NodeJs-5天学习】第四天存储篇④ ——基于MQTT的环境温度检测,升级存储为mysql
  17. 电子商务系统需求分析
  18. paddlepaddle测试安装_专栏 | 新手入门?一步一步教你如何安装PaddlePaddle
  19. Spring Cloud架构教程 (二)Hystrix监控数据聚合
  20. java毕业设计项目源代码javaweb在线商城电商项目|购物

热门文章

  1. OneDrive操作指南
  2. 一些代码和心得记录我的成长经历
  3. Python项目csv导出
  4. Dynamic CRM Plugin
  5. windows编译libzip vs2017
  6. 创建conda虚拟环境
  7. 央视315晚会 惠普“雪花屏”门被搬出 垃圾惠普
  8. 台湾大学郭彦甫matlab讲义,台湾国立大学郭彦甫Matlab教程笔记(17)numerical integration...
  9. 单点登录服务Authelia(下篇)
  10. Python-从笛卡尔坐标插值到极坐标