textCNN在tensorflow上的故事——记一个tf入门者的学习之路
来源:互联网 发布:苹果电脑装机软件 编辑:程序博客网 时间:2024/05/22 13:51
背景
这篇博客主要用来记录一个从不会tensorflow到第一个project(textCNN—中文短文本分类)正式开张的故事,用来与同样刚入门tf的童鞋交流,大神就不必看了:
- 本人有一定机器学习的理论基础,如果你对CNN原理不了解的可以去Coursera上看看Andrew Ng的Machine Learning课程关于NN部分,遇到不懂的百度/Google翻阅博客,基本看一部分再把不懂得琢磨查资料思考后对NN大致会有了解了,然后查查各种博客看看CNN的原理与过程,理论上就大致有个印象了
- 之前在torch上实现了4层CNN对cifar-10的78%准确率识别,之所以没有直接用VGG或者GoogleNet这种已经有的大佬网络,主要是方便自己感受下CNN的搭建使用以及框架的熟悉。tensorflow这次是第一次用。
- 本人仍然是在笔记本(mac pro 2015)上进行textCNN代码试运行后再考虑去台式机带GPU上调参,本子cpu跑3s一个batch,台式带gpu(linux系统)1s三个batch,心累。。
- 主要描述我这种业余选手从不会tf到运行project v1的经历,并不高大上。
- 背景
- tensorflow初次见面
- 正片是啥
- data_helperspy相关解释
- text_cnnpy相关解释
- trainpy相关解释
- 最后贴一个效果
tensorflow初次见面
- 安装:直接去官网,上面有完整指南。个人不建议windows, 在linux、mac、windows都装过,在windows上面第一个程序关于mnist手写识别的就崩了,mac安然无恙。可能是我水平次,但win给我的印象太…
- 官方tf教程:如果英文看起来吃力,给个传送门http://wiki.jikexueyuan.com/project/tensorflow-zh/get_started/introduction.html,中文版,但是像我这种性格官方版本过于官方不够细致和通俗,显然是看不太下的。
- 如果官方版本看不下去,有个B站的系列视频做的不错,传送门在此http://www.bilibili.com/video/av6653880/,不过up主貌似是双系统(刚刚被我黑的win…)。
- 经过理论的饲养(Andrew Ng+各种博客),再加上上述官方文档和视频(不懂再去查资料看博客)的熏陶,基本上对tf这个框架和CNN都有个大致了解了。
- 接下来进行textCNN对短文本分类,因为不像图形处理那样直接数据就是像素矩阵,所以相对麻烦一些,原始代码是Github上的某位大佬的,表示感谢,帮了我大忙链接在此https://github.com/dennybritz/cnn-text-classification-tf
- 感觉使用实际代码来操作几波对textCNN和tensorflow熟悉感增加不少,直接看论文,真的看不太动。
正片是啥
对上述Github上代码的做一些注释,帮助类似我的初学者理解,代码可能有不同,请与原来的代码对比,下面会陆续贴部分代码,主要用到了data_helpers.py、text_cnn.py、train.py,第三个可以看做是主函数
data_helpers.py相关解释
这部分主要是定义了两个函数:
(1)第一个是数据载入函数
输入样本文档,输出样本data、样本label的list,这个没啥好说的。不过值得注意的是,这里作者是一次性将所有样本塞进去内存了,存在一个隐患问题,样本大了后会爆内存,别问我为什么会知道。解决方案可能稍后会做一些对比尝试或许更新博客,比如对超大大样本随机抽样(有放回和无放回到底哪样最合适待验证)出100个不爆内存的样本,然后按照随机顺序放进模型训练,训练仍采用minibatch。(为什么使用minibatch训练,我直接给个链接http://hp.stuhome.net/index.php/2016/09/20/tensorflow_batch_minibatch/,其实大家应该都知道原委)
这部分代码请根据自己的样本文档特征修改,我的就不贴了,可能大家的样本情况格式都不一样。
(2)第二个函数为一个batch样本生成器
def batch_iter(data, batch_size, num_epochs, shuffle=True): """ Generates a batch iterator for a dataset.批量数据batchsize生成器 定义一个函数,输出batch样本,参数为data(包括feature和label),batchsize,epoch """ data = np.array(data)#全部数据转化为array data_size = len(data) num_batches_per_epoch = int((len(data)-1)/batch_size) + 1#每个epoch有多少个batch,个数 for epoch in range(num_epochs): # Shuffle the data at each epoch if shuffle: shuffle_indices = np.random.permutation(np.arange(data_size)) shuffled_data = data[shuffle_indices]# shuffled_data按照上述乱序得到新的样本 else: shuffled_data = data for batch_num in range(num_batches_per_epoch):#开始生成batch start_index = batch_num * batch_size end_index = min((batch_num + 1) * batch_size, data_size)#这里主要是最后一个batch可能不足batchsize的处理 yield shuffled_data[start_index:end_index] #yield,在for循环执行时,每次返回一个batch的data,占用的内存为常数
这里可以看出作者的细心,第一个是end_index = min((batch_num + 1) * batch_size, data_size),考虑最后最后一个batch可能大小不够batchsize了,这在上一篇博客也见到过,第二个是生成器,而不是返回一个包含各个batch样本的list(这一点是否是我多虑了呢,没有仔细观察内存状况对比)
text_cnn.py相关解释
这部分主要是建立了一个text_cnn结构的类
结构比较简单,一个embedding layer+一个convolution layer(Relu)+一个maxpooling层+softmax
主要会引起思考的问题在哪呢,如果你一行行敲代码就会发现:
(1)每个层的参数设置问题
(2)embedding layer和所谓的word2vec是个什么关系
先回答第二个问题,一开始我接触这个project的时候,天真的想,word2vec不就是一个把词的one-hot形式转化为稠密的短向量表示的工具么,用过后一个文本就变成了稠密矩阵,再当图像处理不行么?当然不行!
为什么?
reason one > 如果你仔细看下word2vec的原理,会发现vector其实是一个中间产物,本质上是模型的一部分参数,那意味着什么,不同的训练目标和样本结构得到的最优参数是不一样的,所以不存在固定的vector来表示某个word,具体分析贴一个链接http://spaces.ac.cn/archives/4122/,这是找了数篇资料才发现的解答,贼棒!当然,上面提到的B站up主也有相关解释,也不错。
reason two > 图像处理的卷积核比如3*3,可以横向和纵向两个方向移动,而文本是不行的,因为一个word的表示就是一个横向量,你不能通过原来这种卷积核把一个单词的表示拆开,破坏单词的表示,在这里,一个单词应该和图像的像素点对应即最小单元(但是实际效果我还没去对比过,后续有时间我可能会做以下对比试验),所以卷积核只会在一个维度上移动,比如词的vector为256长度,那么卷积核应该为3*256。
—另外注意他的maxpooling参数设置,好好感受下,利用CNN解决文本分类问题的文章还是很多的,比如这篇 A Convolutional Neural Network for Modelling Sentences 最有意思的输入是在 pooling 改成 (dynamic) k-max pooling ,pooling阶段保留 k 个最大的信息,保留了全局的序列信息,效果我暂时还没去尝试,后续有兴趣了再更新吧。
然后回答第一个问题:
我当时用torch的时候没有遇到这种问题,因为torch写CNN太轻松了,一个卷积层你直接add一个卷积模块就行了,但是tensorflow很细节化,需要你写出参数矩阵w、b的tensor规格,所以这里涉及卷积核参数维度、移动维度等问题,这里接着贴一个链接,也是找了一些资料才发现回答的比较好的。http://blog.csdn.net/hymanyoung/article/details/65444288直接看“TensorFlow卷积神经网络实践”后面的部分,比较清楚的说明了各参数的结构
这一部分我的整体加注释的代码,可能不专业,但希望有助于理解。
import tensorflow as tfimport numpy as npclass TextCNN(object):#定义了1个TEXTCNN的类,包含一张大的graph """ A CNN for text classification. Uses an embedding layer, followed by a convolutional, max-pooling and softmax layer. embedding层,卷积层,池化层,softmax层 """ def __init__( self, sequence_length, num_classes, vocab_size, embedding_size, filter_sizes, num_filters, l2_reg_lambda=0.0):#定义各种输入参数,这里的输入是句子各词的索引? # Placeholders for input, output and dropout self.input_x = tf.placeholder(tf.int32, [None, sequence_length], name="input_x") #定义一个operation,名称input_x,利用参数sequence_length,None表示样本数不定, #不一定是一个batchsize,训练的时候是,验证的时候None不是batchsize #这是一个placeholder, #数据类型int32,(样本数*句子长度)的tensor,每个元素为一个单词 self.input_y = tf.placeholder(tf.float32, [None, num_classes], name="input_y") #这个placeholder的数据输入类型为float,(样本数*类别)的tensor self.dropout_keep_prob = tf.placeholder(tf.float32, name="dropout_keep_prob") #placeholder表示图的一个操作或者节点,用来喂数据,进行name命名方便可视化 # Keeping track of l2 regularization loss (optional) l2_loss = tf.constant(0.0) #l2正则的初始化,有点像sum=0 #其实softmax是需要的 # Embedding layer #参见 with tf.device('/cpu:0'), tf.name_scope("embedding"):#封装了一个叫做“embedding'的模块,使用设备cpu,模块里3个operation self.W = tf.Variable( tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0), name="W")#operation1,一个(词典长度*embedsize)tensor,作为W,也就是最后的词向量 self.embedded_chars = tf.nn.embedding_lookup(self.W, self.input_x) #operation2,input_x的tensor维度为[none,seq_len],那么这个操作的输出为none*seq_len*em_size self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1) #增加一个维度,变成,batch_size*seq_len*em_size*channel(=1)的4维tensor,符合图像的习惯 # Create a convolution + maxpool layer for each filter size pooled_outputs = []#空list for i, filter_size in enumerate(filter_sizes):#比如(0,3),(1,4),(2,5) with tf.name_scope("conv-maxpool-%s" % filter_size):#循环第一次,建立一个名称为如”conv-ma-3“的模块 # Convolution Layer filter_shape = [filter_size, embedding_size, 1, num_filters] #operation1,没名称,卷积核参数,高*宽*通道*卷积个数 W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1), name="W") #operation2,名称”W“,变量维度filter_shape的tensor b = tf.Variable(tf.constant(0.1, shape=[num_filters]), name="b") #operation3,名称"b",变量维度卷积核个数的tensor conv = tf.nn.conv2d( self.embedded_chars_expanded, W, strides=[1, 1, 1, 1],#样本,height,width,channel移动距离 padding="VALID", name="conv") #operation4,卷积操作,名称”conv“,与w系数相乘得到一个矩阵 # Apply nonlinearity h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu") #operation5,加上偏置,进行relu,名称"relu" # Maxpooling over the outputs pooled = tf.nn.max_pool( h, ksize=[1, sequence_length - filter_size + 1, 1, 1], strides=[1, 1, 1, 1], padding='VALID', name="pool") pooled_outputs.append(pooled) #每个卷积核和pool处理一个样本后得到一个值,这里维度如batchsize*1*1*卷积核个数 #三种卷积核,appen3次 # Combine all the pooled features num_filters_total = num_filters * len(filter_sizes) #operation,每种卷积核个数与卷积核种类的积 self.h_pool = tf.concat(pooled_outputs, 3) #operation,将outpus在第4个维度上拼接,如本来是128*1*1*64的结果3个,拼接后为128*1*1*192的tensor self.h_pool_flat = tf.reshape(self.h_pool, [-1, num_filters_total]) #operation,结果reshape为128*192的tensor # Add dropout with tf.name_scope("dropout"): self.h_drop = tf.nn.dropout(self.h_pool_flat, self.dropout_keep_prob) #添加一个"dropout"的模块,里面一个操作,输出为dropout过后的128*192的tensor # Final (unnormalized) scores and predictions with tf.name_scope("output"):#添加一个”output“的模块,多个operation W = tf.get_variable( "W", shape=[num_filters_total, num_classes], initializer=tf.contrib.layers.xavier_initializer()) #operation1,系数tensor,如192*2,192个features分2类,名称为"W",注意这里用的是get_variables b = tf.Variable(tf.constant(0.1, shape=[num_classes]), name="b") #operation2,偏置tensor,如2,名称"b" l2_loss += tf.nn.l2_loss(W) #operation3,loss上加入w的l2正则 l2_loss += tf.nn.l2_loss(b) #operation4,loss上加入b的l2正则 self.scores = tf.nn.xw_plus_b(self.h_drop, W, b, name="scores") #operation5,scores计算全连接后的输出,如[0.2,0.7]名称”scores“ self.predictions = tf.argmax(self.scores, 1, name="predictions") #operations,计算预测值,输出最大值的索引,0或者1,名称”predictions“ # CalculateMean cross-entropy loss with tf.name_scope("loss"):#定义一个”loss“的模块 losses = tf.nn.softmax_cross_entropy_with_logits(logits=self.scores, labels=self.input_y) #operation1,定义losses,交叉熵,如果是一个batch,那么是一个长度为batchsize1的tensor? self.loss = tf.reduce_mean(losses) + l2_reg_lambda * l2_loss #operation2,计算一个batch的平均交叉熵,加上全连接层参数的正则 # Accuracy with tf.name_scope("accuracy"):#定义一个名称”accuracy“的模块 correct_predictions = tf.equal(self.predictions, tf.argmax(self.input_y, 1)) #operation1,根据input_y和predictions是否相同,得到一个矩阵batchsize大小的tensor self.accuracy = tf.reduce_mean(tf.cast(correct_predictions, "float"), name="accuracy") #operation2,计算均值即为准确率,名称”accuracy“
train.py相关解释
就是主函数了,输入样本和超参数,训练网络、存储参数、计算准确率等,带“summary”的函数或者模块是tensorflow储存参数和可视化的工具,我暂时也不怎么熟练,就没怎么写注释,不过不影响功能。
这一节没什么大的引起思考的问题,加了一点点注释和改了一点点参数:
import tensorflow as tfimport numpy as npimport osimport timeimport datetimeimport data_helpersfrom text_cnn import TextCNNfrom tensorflow.contrib import learn# Parameters# ==================================================# Data loading params,#数据集里10%为验证集tf.flags.DEFINE_float("dev_sample_percentage", .1, "Percentage of the training data to use for validation")#原数据的文件路径tf.flags.DEFINE_string("data_file", "/Users/xuhy/Downloads/cnn-text-wujun/data/after_fenci_wujuncnndata", "Data source.")# Model Hyperparameters#embedding维度256,4种卷积核,每种128个,0.5的dropouttf.flags.DEFINE_integer("embedding_dim", 256, "Dimensionality of character embedding (default: 128)")tf.flags.DEFINE_string("filter_sizes", "2,3,4,5", "Comma-separated filter sizes (default: '2,3,4,5')")tf.flags.DEFINE_integer("num_filters", 128, "Number of filters per filter size (default: 128)")tf.flags.DEFINE_float("dropout_keep_prob", 0.5, "Dropout keep probability (default: 0.5)")tf.flags.DEFINE_float("l2_reg_lambda", 0.0, "L2 regularization lambda (default: 0.0)")# Training parameters#batchsize为64,20个epoch,每100个batch后,计算验证集上的表现,每100个batch后保存模型,checkpoint是个啥?tf.flags.DEFINE_integer("batch_size", 64, "Batch Size (default: 64)")tf.flags.DEFINE_integer("num_epochs", 20, "Number of training epochs (default: 200)")tf.flags.DEFINE_integer("evaluate_every", 100, "Evaluate model on dev set after this many steps (default: 100)")tf.flags.DEFINE_integer("checkpoint_every", 100, "Save model after this many steps (default: 100)")tf.flags.DEFINE_integer("num_checkpoints", 5, "Number of checkpoints to store (default: 5)")# Misc Parameters#true表示自动寻找一个存在并支持的cpu或者gpu,防止指定的设备不存在#如果将False改为True,可以看到operations被指派到哪个设备运行tf.flags.DEFINE_boolean("allow_soft_placement", True, "Allow device soft device placement")tf.flags.DEFINE_boolean("log_device_placement", False, "Log placement of ops on devices")FLAGS = tf.flags.FLAGS#FLAGS是一个对象,保存了解析后的命令行参数FLAGS._parse_flags()print("\nParameters:")for attr, value in sorted(FLAGS.__flags.items()): print("{}={}".format(attr.upper(), value))print("")# Data Preparation# ==================================================# Load dataprint("Loading data...")print("start_time"+"\t\t"+str(datetime.datetime.now().isoformat()))x_text, y = data_helpers.load_data_and_labels(FLAGS.data_file)print("end_time"+"\t\t"+str(datetime.datetime.now().isoformat()))#这里的y是数值,x还是单词序列#!!!这里一次性载入所有数据,注意考虑内存,大数据的情况下如何载入需要分析# Build vocabularyprint("生成单词索引,构成样本索引矩阵...")print("start_time"+"\t\t"+str(datetime.datetime.now().isoformat()))max_document_length = 298#每一条评价的最多单词数字vocab_processor = learn.preprocessing.VocabularyProcessor(max_document_length)#单词转化为在字典中的位置,这是一个操作x = np.array(list(vocab_processor.fit_transform(x_text)))y = np.array(y)print("end_time"+"\t\t"+str(datetime.datetime.now().isoformat()))#在不够长度的评价最后加0,样本变成了索引数值矩阵,这里的x已经是索引序列了,n*seq_len的tensor# Randomly shuffle dataprint("打乱样本顺序...")print("start_time"+"\t\t"+str(datetime.datetime.now().isoformat()))np.random.seed(10)shuffle_indices = np.random.permutation(np.arange(len(y)))#打乱样本x_shuffled = x[shuffle_indices]#新的乱序样本y_shuffled = y[shuffle_indices]#新的乱序labelprint("end_time"+"\t\t"+str(datetime.datetime.now().isoformat()))# Split train/test set# TODO: This is very crude, should use cross-validation训练集、验证集划分完毕,全部是索引数值print("生成训练集和验证集...")print("start_time"+"\t\t"+str(datetime.datetime.now().isoformat()))dev_sample_index = -1 * int(FLAGS.dev_sample_percentage * float(len(y)))#负数,倒过来数x_train, x_dev = x_shuffled[:dev_sample_index], x_shuffled[dev_sample_index:]#切片y_train, y_dev = y_shuffled[:dev_sample_index], y_shuffled[dev_sample_index:]print("Vocabulary Size: {:d}".format(len(vocab_processor.vocabulary_)))#字典长度print("Train/Dev split: {:d}/{:d}".format(len(y_train), len(y_dev)))#训练集和验证集长度print("end_time"+"\t\t"+str(datetime.datetime.now().isoformat()))# Training# ==================================================with tf.Graph().as_default(): session_conf = tf.ConfigProto( allow_soft_placement=FLAGS.allow_soft_placement, log_device_placement=FLAGS.log_device_placement)#这个session配置,按照前面的gpu,cpu自动选择 sess = tf.Session(config=session_conf)#建立一个配置如上的会话 with sess.as_default():#在上述session填充内容 cnn = TextCNN( sequence_length=x_train.shape[1],#[0]是样本维度,样本数量,[1]是单个样本的长度 num_classes=y_train.shape[1],#同理,这里是类别数量 vocab_size=len(vocab_processor.vocabulary_),#字典长度 embedding_size=FLAGS.embedding_dim, filter_sizes=list(map(int, FLAGS.filter_sizes.split(","))), num_filters=FLAGS.num_filters, l2_reg_lambda=FLAGS.l2_reg_lambda) #包含一个CNN #TextCNN是一个类,输入参数,得到一个CNN结构 # Define Training procedure global_step = tf.Variable(0, name="global_step", trainable=False)#定义一个变量step optimizer = tf.train.AdamOptimizer(1e-3)#里面是学习速率,选择优化算法,建立优化器 grads_and_vars = optimizer.compute_gradients(cnn.loss)#选择目标函数,计算梯度;返回的是梯度和变量 #函数minimize() 与compute_gradients()都含有一个参数gate_gradient,用于控制在应用这些梯度时并行化的程度。这里没有? train_op = optimizer.apply_gradients(grads_and_vars, global_step=global_step)#运用梯度
中间summary环节我就没怎么写,就不贴了,直接帖后面的。
# Initialize all variables sess.run(tf.global_variables_initializer())#初始化所有变量 #定义了一个函数,输入为1个batch def train_step(x_batch, y_batch): """ A single training step """ feed_dict = { cnn.input_x: x_batch, cnn.input_y: y_batch, cnn.dropout_keep_prob: FLAGS.dropout_keep_prob } _, step, summaries, loss, accuracy = sess.run( [train_op, global_step, train_summary_op, cnn.loss, cnn.accuracy], feed_dict) #梯度更新(更新模型),步骤加一,存储数据,计算一个batch的损失,计算一个batch的准确率 time_str = datetime.datetime.now().isoformat()#当时时间 print("{}: step {}, loss {:g}, acc {:g}".format(time_str, step, loss, accuracy)) train_summary_writer.add_summary(summaries, step) #定义了一个函数,用于验证集,输入为一个batch def dev_step(x_batch, y_batch, writer=None): """ Evaluates model on a dev set """ #验证集太大,会爆内存,采用batch的思想进行计算,下面生成多个子验证集 num=20 x_batch=x_batch.tolist() y_batch=y_batch.tolist() l=len(y_batch) l_20=int(l/num) x_set=[] y_set=[] for i in range(num-1): x_temp=x_batch[i*l_20:(i+1)*l_20] x_set.append(x_temp) y_temp=y_batch[i*l_20:(i+1)*l_20] y_set.append(y_temp) x_temp=x_batch[(num-1)*l_20:] x_set.append(x_temp) y_temp=y_batch[(num-1)*l_20:] y_set.append(y_temp) #每个batch验证集计算一下准确率,num个batch再平均 lis_loss=[] lis_accu=[] for i in range(num): feed_dict = { cnn.input_x: np.array(x_set[i]), cnn.input_y: np.array(y_set[i]), cnn.dropout_keep_prob: 1.0 } step, summaries, loss, accuracy = sess.run( [global_step, dev_summary_op, cnn.loss, cnn.accuracy], feed_dict) lis_loss.append(loss) lis_accu.append(accuracy) time_str = datetime.datetime.now().isoformat() print("{}: step {}, loss {:g}, acc {:g}".format(time_str, step, loss, accuracy)) print("test_loss and test_acc"+"\t\t"+str(sum(lis_loss)/num)+"\t\t"+str(sum(lis_accu)/num)) if writer: writer.add_summary(summaries, step) # Generate batches(生成器),得到一个generator,每一次返回一个batch,没有构成list[batch1,batch2,batch3,...] batches = data_helpers.batch_iter( list(zip(x_train, y_train)), FLAGS.batch_size, FLAGS.num_epochs) #zip将样本与label配对, # Training loop. For each batch... for batch in batches: x_batch, y_batch = zip(*batch)#unzip,将配对的样本,分离出来data和label train_step(x_batch, y_batch)#训练,输入batch样本,更新模型 current_step = tf.train.global_step(sess, global_step) if current_step % FLAGS.evaluate_every == 0:#每多少步,算一下验证集效果 print("\nEvaluation:") dev_step(x_dev, y_dev, writer=dev_summary_writer)#喂的数据为验证集,此时大小不止一个batchsize1的大小 print("") if current_step % FLAGS.checkpoint_every == 0:#每多少步,保存模型 path = saver.save(sess, checkpoint_prefix, global_step=current_step) print("Saved model checkpoint to {}\n".format(path))
这里当时遇到了一个问题,就是每次验证集计算的时候,内存就爆了,然后我就采用minibatch的想法改进了以下,没毛病了。
最后贴一个效果
初级版本,后续会尽量优化
目前,23w+样本,类别25+,下面是训练不到2个epoch的情况,测试集准确率86.17%:
test_loss and test_acc 0.475186523795 0.861730447412
Saved model checkpoint to /Users/xuhy/runs/1498792747/checkpoints/model-6800
后面有空更。
谢谢!
- textCNN在tensorflow上的故事——记一个tf入门者的学习之路
- tensorflow学习之路(2-1):tf.variable_scope(), tf.name_scope(), tf.get_variable()的认识
- tensorflow学习之路(2-2):tf.variable_scope(),tf.name_scope(),tf.get_variable()的认识(补充)
- Tensorflow深度学习入门——TF自带的数据文件读取及下载
- tensorflow学习之路(6):tf.strided_slice()和tf.cast()的认识
- tf.reduce_sum tensorflow维度上的操作
- TensorFlow学习笔记之tf.nn.softmax()与tf.nn.softmax_cross_entropy_with_logits的用法
- 【tensorflow 学习】tf.get_variable()和tf.Variable()的区别
- textcnn自己的理解
- TensorFlow学习---tf生成数据的方法
- TensorFlow学习---tf.nn.softmax_cross_entropy_with_logits的用法
- TensorFlow学习之路——入门
- TensorFlow 学习(一)—— tf.get_variable() vs tf.Variable(),tf.name_scope() vs tf.variable_scope()
- tensorflow学习——tf.get_collection(), tf.identity()
- tensorflow学习——tf.floor与tf.train.batch
- TensorFlow 官方文档中文版解读之1 ——tf.concat的用法的用法
- tensorflow API学习——tf.strided_slice
- AI学习之路(2):GPU版本的Tensorflow在Windows上安装
- Tomcat启动慢的测试与解决
- SharedPerferences 工具类
- JS闭包(closure)一个应用示例
- sqlite3 数据去重与通配符
- 三,我的大学生活(2)--新的开始
- textCNN在tensorflow上的故事——记一个tf入门者的学习之路
- 通向架构师的道路(第一天)之Apache整合Tomcat
- 'org.springframework.beans.MutablePropertyValues.get(Ljava/lang/ String;)Ljava/lang/Object;'. See E
- Eclipse中导入外部项目时提示HttpServletRequest 不能引用的问题
- [8] Shell 基础知识
- 基于HTML5 画布功能canvas的绘画板:画线、画圆、画矩形、橡皮、改变线条
- [树的点分治] 不虚就是要AK
- 通向架构师的道路(第二天)之apache tomcat https应用
- 数据库存储引擎学习总结