2018年10月14日笔记

tensorflow是谷歌google的深度学习框架,tensor中文叫做张量,flow叫做流。
CNN是convolutional neural network的简称,中文叫做卷积神经网络。
文本分类是NLP(自然语言处理)的经典任务。
项目成果如下图所示:

10345471-8e5645608a0c8747.png

image.png

清华论文使用技术:bigram特征选取、Chi-square特征降维、tfidf权重计算、LibSVM模型,
2016年论文,链接:http://nlp.csai.tsinghua.edu.cn/site2/index.php/zh/codes/242-thuctc
我使用技术:word-embedding、CNN
使用CNN做文本分类比传统机器学习方法提高precision值0.05,F1score值0.08
因为CNN不需要分词,训练tfidf模型,程序运行时间节省10倍以上。

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会提高计算速度50倍。
安装教程链接:https://blog.csdn.net/qq_36556893/article/details/79433298
如果没有nvidia显卡,但有visa信用卡,请阅读我的另一篇文章《在谷歌云服务器上搭建深度学习平台》,链接:https://www.jianshu.com/p/893d622d1b5a

3.下载并解压数据集

两种下载方式效果相同:
1.官方数据集下载链接: http://thuctc.thunlp.org/message
2.百度云盘下载链接: https://pan.baidu.com/s/1OC2uMoVHAWtX4mfOv6urCg 提取码: qphu
数据集大小:1.45GB
样本数量:80多万
数据集详情链接:http://thuctc.thunlp.org
压缩文件THUCNews.zip选择解压到当前文件夹,如下图所示。

10345471-cdef8957f7a36dd6.png

image.png

使用BandZip解压需要大约25分钟
使用7zip解压需要大约50分钟
使用WinRAR解压需要大约100分钟
所以推荐读者下载并安装BandZip解压软件,下载链接:http://www.bandisoft.com/bandizip/
进入下载页面后,下载按钮如下图所示:

10345471-6613536b36957b8f.png

image.png

压缩文件夹THUCNews中有14个文件夹,如下图所示:

10345471-a812263f077549bb.png

image.png

14个新闻类别中打开财经文件夹,如下图所示:

10345471-5d06fd767709c58b.png

image.png

从上面的图中可以看出,每个类别文件夹中有若干文本文件,每个文本文件中内容表示1篇新闻。

4.获取数据

4.1 获取文本文件路径

import osdef getFilePathList(rootDir):filePath_list = []for walk in os.walk(rootDir):part_filePath_list = [os.path.join(walk[0], file) for file in walk[2]]filePath_list.extend(part_filePath_list)return filePath_list
filePath_list = getFilePathList('THUCNews')
len(filePath_list)

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

836062

从上面的运行结果可以看出,文件路径列表中共有836062个元素,即在THUCNews文件夹中总共有836062个文本文件。

4.2 获取所有样本标签

Windows和Linux系统路径字符串的间隔符有区别,本文作者是Windows系统。

label_list = []
for filePath in filePath_list:label = filePath.split('\\')[1]label_list.append(label)
len(label_list)

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

836062

所有样本标签的值汇总成一个列表,赋值给标签列表label_list。
从上面的运行结果可以看出,标签列表中共有836062个元素。

4.3 标签统计计数

import pandas as pdpd.value_counts(label_list)

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

10345471-c071b59ea87b7428.png

image.png

4.4 调用pickle库保存label_list

import picklewith open('label_list.pickle', 'wb') as file:pickle.dump(label_list, file)

4.5 获取所有样本内容、保存content_list

import time
import pickle
import redef getFile(filePath):with open(filePath, encoding='utf8') as file:fileStr = ''.join(file.readlines(1000))return fileStrinterval = 20000
n_samples = len(label_list)
startTime = time.time()
directory_name = 'content_list'
if not os.path.isdir(directory_name):os.mkdir(directory_name)
for i in range(0, n_samples, interval):startIndex = iendIndex = i + intervalcontent_list = []print('%06d-%06d start' %(startIndex, endIndex))for filePath in filePath_list[startIndex:endIndex]:fileStr = getFile(filePath)content = re.sub('\s+', ' ', fileStr)content_list.append(content)save_fileName = directory_name + '/%06d-%06d.pickle' %(startIndex, endIndex)with open(save_fileName, 'wb') as file:pickle.dump(content_list, file)used_time = time.time() - startTimeprint('%06d-%06d used time: %.2f seconds' %(startIndex, endIndex, used_time))

pickle库的dump方法可以将python中对象持久化为二进制文件,二进制文件的加载速度非常快。
避免内存溢出,每读取一定数量的文件就利用pickle库的dump方法保存。
因为有80多万个文本文件,读取时间较长。
本文作者使用的是固态硬盘,所以读取速度可以达到170M/s左右,如果是机械硬盘,可能只有80M/s左右。

10345471-d8795b3cdfc5d05a.png

image.png

上面一段代码的运行结果如下,为了节省篇幅,只显示前10行。
下面是读取前10万个文件的提示信息,总共花费64秒
读取总共80万个文件,本文作者花费时间600秒

000000-020000 start
000000-020000 used time: 8.50 seconds
020000-040000 start
020000-040000 used time: 20.80 seconds
040000-060000 start
040000-060000 used time: 35.67 seconds
060000-080000 start
060000-080000 used time: 50.78 seconds
080000-100000 start
080000-100000 used time: 65.15 seconds

5.加载数据

本文前面的第3章下载并解压数据集、第4章获取数据记录了拿到原始数据的处理过程。
处理过程比较耗费时间,本文作者将前2章的处理结果上传到网盘中,即给读者提供复现此项目的捷径。
百度云盘下载链接: https://pan.baidu.com/s/1BB4LFt71MK0XQ24lMTTQxg 提取码: k9hy
pickle格式文件是二进制文件,加载速度是文本文件的50倍以上。
压缩文件清华新闻分类已处理数据.zip,解压时选择解压到当前文件夹
请务必确保content_list文件夹label_list文件代码文件这3者处于相同路径,代码才能运行成功。

import time
import pickle
import osdef getFilePathList(rootDir):filePath_list = []for walk in os.walk(rootDir):part_filePath_list = [os.path.join(walk[0], file) for file in walk[2]]filePath_list.extend(part_filePath_list)return filePath_liststartTime = time.time()
contentListPath_list = getFilePathList('content_list')
content_list = []
for filePath in contentListPath_list:with open(filePath, 'rb') as file:part_content_list = pickle.load(file)content_list.extend(part_content_list)
with open('label_list.pickle', 'rb') as file:label_list = pickle.load(file)
used_time = time.time() - startTime
print('used time: %.2f seconds' %used_time)
sample_size = len(content_list)
print('length of content_list,mean sample size: %d' %sample_size)

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

used time: 8.36 seconds
length of content_list,mean sample size: 836062

6.词汇表

6.1 制作词汇表

内容列表content_list中的元素是每篇文章内容,数据类型为字符串。
所有文章内容中的字做统计计数,出现次数排名前10000的字赋值给变量vocabulary_list。

from collections import Counter
def 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 ['PAD'] + vocabulary_list
startTime = time.time()
vocabulary_list = getVocabularyList(content_list, 10000)
used_time = time.time() - startTime
print('used time: %.2f seconds' %used_time)

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

used time: 97.52 seconds

6.2 保存词汇表

import pickle with open('vocabulary_list.pickle', 'wb') as file:pickle.dump(vocabulary_list, file)

6.3 加载词汇表

每次制作词汇表都得花费90秒,即1.5分钟左右。
完成制作词汇表后,将其保存,之后再运行代码则直接加载保存的词汇表,节省了复制作词汇表花费的时间。

import picklewith open('vocabulary_list.pickle', 'rb') as file:vocabulary_list = pickle.load(file)

7.数据准备

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

10345471-9c59d88a0ddca06f.png

image.png

在命令模式下,点击L键,会显示代码行数。
推荐博客《Text-CNN 文本分类》从模型原理上辅助理解,链接:https://blog.csdn.net/chuchus/article/details/77847476
第1行代码导入time库;
第2行代码记录本段代码运行开始时间,赋值给变量startTime;
第3行代码导入sklearn.model_selection库的train_test_split方法;
第4行代码导入调用sklearn.model_selection库的train_test_split方法划分训练集、测试集;
第5-8行代码获取训练集文本内容列表train_content_list,训练集标签列表train_label_list,测试集文本内容列表test_content_list,测试集标签列表test_label_list。
第9-10行代码打印提示信息,表示程序运行至此步花费时间;
第11-19行代码设置模型的9个超参数
第20行代码使用列表推导式得到词汇及其id对应的列表,并调用dict方法将列表强制转换为字典。
打印变量word2id_dict的前5项,如下图所示:

10345471-ff587e2dcb9a376b.png

image.png

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

import time
startTime = time.time()
from sklearn.model_selection import train_test_split
train_X, test_X, train_y, test_y = train_test_split(content_list, label_list)
train_content_list = train_X
train_label_list = train_y
test_content_list = test_X
test_label_list = test_y
used_time = time.time() - startTime
print('train_test_split used time : %.2f seconds' %used_time)
vocabulary_size = 10000  # 词汇表达小
sequence_length = 600  # 序列长度
embedding_size = 64  # 词向量维度
num_filters = 256  # 卷积核数目
filter_size = 5  # 卷积核尺寸
num_fc_units = 128  # 全连接层神经元
dropout_keep_probability = 0.5  # dropout保留比例
learning_rate = 1e-3  # 学习率
batch_size = 64  # 每批训练大小
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]
used_time = time.time() - startTime
print('content2idList used time : %.2f seconds' %used_time)
import numpy as np
num_classes = np.unique(label_list).shape[0]
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])
used_time = time.time() - startTime
print('data preparation used time : %.2f seconds' %used_time)

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

train_test_split used time : 0.61 seconds
content2idList used time : 121.08 seconds
data preparation used time : 175.48 seconds

代码进行到此步,python进程占用6个多G内存,如下图所示。
所以此项目需要较高的机器配置,如果读者的电脑内存不足可以通过下面2种方法解决:
1.购买内存条提高机器配置,本文作者建议使用此方式,省心省力。
2.将阶段性结果保存在本地,重启python,读取阶段性结果。
3.不一次性处理全部样本,样本分批处理好之后再汇总。

10345471-a992d340680a32a6.png

image.png

8.搭建神经网络

第1-2行代码调用tf库的get_variable方法实例化可以更新的模型参数embedding,矩阵形状为vocab_size*embedding_size,即5000*64
第3-4行代码调用tf.nn库的embedding_lookup方法将输入数据做词嵌入,得到新变量embedding_inputs的形状为batch_size*sequence_length*embedding_size,即64*600*64
理解word2vec原理,推荐阅读文章链接:https://www.jianshu.com/p/471d9bfbd72f
第5-7行代码调用tf.layers.conv1d方法,方法需要3个参数,第1个参数是输入数据,第2个参数是卷积核数量num_filters,第3个参数是卷积核大小filter_size。方法结果赋值给变量conv,形状为batch_size*596*num_filters,596是600-5+1的结果;
第8-9行代码调用tf.reduce_max方法对变量conv的第1个维度做求最大值操作。方法结果赋值给变量max_pooling,形状为batch_size*num_filters,即64*256
第10-11行代码添加全连接层,tf.layers.dense方法结果赋值给变量full_connect,形状为batch_size*num_fc_units,即64*128
第12-13行代码调用tf.contrib.layers.dropout方法,方法需要2个参数,第1个参数是输入数据,第2个参数是保留比例;
第14行代码调用tf.nn.relu方法,即激活函数;
第15-16行代码添加全连接层,tf.layers.dense方法结果赋值给变量softmax_before,形状为batch_size*num_classes,即64*14
第17行代码调用tf.nn.softmax方法,方法结果是预测概率值;
第18-20行代码使用交叉熵作为损失函数;
第21行代码调用tf.train.AdamOptimizer方法定义优化器optimizer;
第22行代码调用优化器对象的minimize方法,即最小化损失;
第23-24行代码计算预测准确率;

embedding = tf.get_variable('embedding', [vocabulary_size, embedding_size])
embedding_inputs = tf.nn.embedding_lookup(embedding,X_holder)
conv = tf.layers.conv1d(embedding_inputs,num_filters,filter_size)
max_pooling = tf.reduce_max(conv, [1])
full_connect = tf.layers.dense(max_pooling,num_fc_units)
full_connect_dropout = tf.contrib.layers.dropout(full_connect, keep_prob=dropout_keep_probability)
full_connect_activate = tf.nn.relu(full_connect_dropout)
softmax_before = tf.layers.dense(full_connect_activate,num_classes)
predict_Y = tf.nn.softmax(softmax_before)
cross_entropy = tf.nn.softmax_cross_entropy_with_logits_v2(labels=Y_holder,logits=softmax_before)
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))

9.参数初始化

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

10345471-675b492bd6c8bcbe.png

image.png

第2行代码调用tf.Session方法实例化会话对象;
第3行代码调用tf.Session对象的run方法做变量初始化。

init = tf.global_variables_initializer()
session = tf.Session()
session.run(init)

10.模型训练

第1-4行代码获取测试集中的数据;
第5行代码导入random库;
第6行表示模型迭代训练20000次;
第7-9行代码从训练集中选取batch_size大小,即64个样本做批量梯度下降
第10行代码每运行1次,表示模型训练1次;
第11行代码记录当前步数,赋值给变量step;
第12行代码表示每间隔100步打印;
第13-15行代码从测试集中随机选取200个样本;
第16行代码表示计算损失值loss_value、准确率accuracy_value;
第17行代码表示打印步数step、损失值loss_value、准确率accuracy_value。

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)
import random
for i in range(20000):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))

上面一段代码的运行结果如下,为了节省文章篇幅,只打印前1000次迭代:

step:100 loss:1.5357 accuracy:0.4900
step:200 loss:1.0189 accuracy:0.7000
step:300 loss:0.7720 accuracy:0.7850
step:400 loss:0.6589 accuracy:0.8000
step:500 loss:0.4987 accuracy:0.8300
step:600 loss:0.5863 accuracy:0.8100
step:700 loss:0.5242 accuracy:0.8350
step:800 loss:0.3541 accuracy:0.9000
step:900 loss:0.5004 accuracy:0.8700
step:1000 loss:0.3152 accuracy:0.9050

代码进行到此步,python进程占用9个多G内存,如下图所示:

10345471-c9c1fda5b35c6248.png

image.png

11.混淆矩阵

此段代码主要是调用sklearn.metrics库的confusion_matrix方法得出混淆矩阵。

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-e4726658f912e79a.png

image.png

12.报告表

此段代码主要是调用sklearn.metrics库的precision_recall_fscore_support方法得出报告表。

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-a9ed869098f2e8fa.png

image.png

13.总结

1.本文是作者第8个NLP项目,数据共有80多万条。
2.分类模型的评估指标F1score为0.93左右,总体来说这个分类模型比较优秀,能够投入实际应用。
3.因为本项目工程量较大和运行时间较久,有较好的效果时,本文作者就抓紧记录,所以文章有很多不完善的地方。后续优化工作可以从解决样本不均衡问题开展,使用下采样或下采样方法。

基于tensorflow、CNN、清华数据集THUCNews的新浪新闻文本分类相关推荐

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

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

  2. 基于tensorflow+RNN的新浪新闻文本分类

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

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

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

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

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

  5. 基于TensorFlow和mnist数据集的手写数字识别系统 ,可识别电话号码,识别准确率高,有对比实验,两组模型,可讲解代码

    基于TensorFlow和mnist数据集的手写数字识别系统 ,可识别电话号码,识别准确率高,有对比实验,两组模型,可讲解代码

  6. 自然语言处理(二)基于CNN的新闻文本分类

    自然语言处理(二) 1.Task1 数据集探索 1.1下载数据集 1.2数据集的描述 1.3 数据的预处理 1.4 CNN卷积神经网络 1.5 训练与验证 2.IMDB 2.1下载 IMDB 数据集 ...

  7. ML之NB:基于NB朴素贝叶斯算法训练20类新闻文本数据集进行多分类预测

    ML之NB:基于NB朴素贝叶斯算法训练20类新闻文本数据集进行多分类预测 目录 输出结果 设计思路 核心代码 输出结果 设计思路 核心代码 vec = CountVectorizer() X_trai ...

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

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

  9. 朴素贝叶斯(西瓜数据集分类,社区恶意留言分类,垃圾邮件分类,新浪新闻分类),AODE分类器 代码实现

    朴素贝叶斯(西瓜数据集分类,社区恶意留言分类,垃圾邮件分类,新浪新闻分类),AODE分类器 代码实现 以下代码为本人学习后,修改或补充后的代码实现,数据集和原代码请参考:https://github. ...

最新文章

  1. static关键字了解解析
  2. 组态王与c语言混合编程6,亚控 组态王嵌入版6.1
  3. 游戏服某个服外网玩家连不上,内网才能连
  4. MATLAB从入门到精通-MATLAB零矩阵eye()函数的几种应用场景
  5. Java并发-Fork/Join框架
  6. Sql Server 2005如何设置连接加密
  7. java培训第一阶段测试总结,达内学员Java培训阶段总结:反躬自省,愈渐完美
  8. 小黑小波比.Ubuntu下的截图
  9. 为什么现在越来越多的人不愿换新机?最后一个原因扎心了
  10. ios pan手势滑动消失动画_解析Color OS全面屏手势,操作丝滑,操作逻辑帮了大忙...
  11. 【洛谷 P1772】 [ZJOI2006]物流运输(Spfa,dp)
  12. 在VS2005中设计WinForms应用程序已完成设计的界面突然丢失的解决
  13. Aizu - 2249
  14. android计算器表格布局,Android 计算器UI-TableLayout
  15. 电脑如何录制正在播放的视频 这个方法太简单了
  16. 利用SPSS做数据分析②之数据处理1
  17. 22个Python绘图包,极简总结
  18. 华为网络设备-NAT实验
  19. 苦才是人生的经典美言
  20. STM32L476低功耗—进入STOP2模式4s后LPTIM中断唤醒+功率实测

热门文章

  1. Landsat 7两个热红外波段B61和B62的区别与应用时的选择方法
  2. 第20讲:Mybatis 中 XML 文件是如何解析的?
  3. HDLbits笔记-移位寄存器
  4. 边缘控制平面Ambassador全解读
  5. [转帖]我的2006
  6. Casein-PEG-Rhodamine B 络蛋白-聚乙二醇-罗丹明B Casein-RB
  7. 10个开源的顶级人工智能工具
  8. 药学薛定谔专题干货总结
  9. linux字符界面忘记登录密码,个性化修改Linux登录时的字符界面
  10. matlab:print命令保存eps文件与figure文件显示不同之解决方法