A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

开篇

好久没有更新Tensorflow与NLP系列了,时间一长就比较容易遗忘,所以今天还是要开始这些源码的解读。老规矩,原理还是一带而过,重要的是代码的解读,我相信整个代码完整的流程掌握了,原理就不在话下了。

整个模型的流程在图上都有完整的体现。

train.py参数设置

首先是大量的参数设置

# Data loading paramstf.flags.DEFINE_float("dev_sample_percentage", .1, "Percentage of the training data to use for validation")tf.flags.DEFINE_string("positive_data_file", "./data/rt-polaritydata/rt-polarity.pos", "Data source for the positive data.")tf.flags.DEFINE_string("negative_data_file", "./data/rt-polaritydata/rt-polarity.neg", "Data source for the negative data.")# Model Hyperparameterstf.flags.DEFINE_integer("embedding_dim", 128, "Dimensionality of character embedding (default: 128)")tf.flags.DEFINE_string("filter_sizes", "3,4,5", "Comma-separated filter sizes (default: '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 parameterstf.flags.DEFINE_integer("batch_size", 64, "Batch Size (default: 64)")tf.flags.DEFINE_integer("num_epochs", 200, "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 Parameterstf.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._parse_flags()# print("\nParameters:")# for attr, value in sorted(FLAGS.__flags.items()):#     print("{}={}".format(attr.upper(), value))# print("")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

参数的设置函数主要有三个参数,参数的名字,参数的默认值,以及参数的解释。这里打印参数的代码被注释了。为什么要这么设置参数呢,因为这样我们可以通过命令行传入我们想要传入的参数,而不需要改动我们的代码。

这里还是要放上源码的地址,以备我忘记github

preprocessdef preprocess():    # Data Preparation    # ==================================================    # Load data    print("Loading data...")    x_text, y = data_helpers.load_data_and_labels(FLAGS.positive_data_file, FLAGS.negative_data_file)    # Build vocabulary    max_document_length = max([len(x.split(" ")) for x in x_text])    vocab_processor = learn.preprocessing.VocabularyProcessor(max_document_length)    x = np.array(list(vocab_processor.fit_transform(x_text)))    # Randomly shuffle data    np.random.seed(10)    shuffle_indices = np.random.permutation(np.arange(len(y)))    x_shuffled = x[shuffle_indices]    y_shuffled = y[shuffle_indices]    # Split train/test set    # TODO: This is very crude, should use cross-validation    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:]    del x, y, x_shuffled, y_shuffled    print("Vocabulary Size: {:d}".format(len(vocab_processor.vocabulary_)))    print("Train/Dev split: {:d}/{:d}".format(len(y_train), len(y_dev)))    return x_train, y_train, vocab_processor, x_dev, y_dev

关于预处理的代码,先是加载数据的代码,我直接放上相应的函数,没有什么特别的可以讲,就是一个加载数据的函数。

def load_data_and_labels(positive_data_file, negative_data_file):    """    Loads MR polarity data from files, splits the data into words and generates labels.    Returns split sentences and labels.    """    # Load data from files    positive_examples = list(open(positive_data_file, "r", encoding='utf-8').readlines())    positive_examples = [s.strip() for s in positive_examples]    negative_examples = list(open(negative_data_file, "r", encoding='utf-8').readlines())    negative_examples = [s.strip() for s in negative_examples]    # Split by words    x_text = positive_examples + negative_examples    x_text = [clean_str(sent) for sent in x_text]    # Generate labels    positive_labels = [[0, 1] for _ in positive_examples]    negative_labels = [[1, 0] for _ in negative_examples]    y = np.concatenate([positive_labels, negative_labels], 0)    return [x_text, y]

值得一提的就是它返回的值,x_text是一个由每句词的列表组成的列表,y的话是由一个长度为2的列表组成的列表。

预处理的第二步就是构建词典,把我们的句子序列(由单词列表构成)转换成数据序列(单词在词典里面的索引),这边完全由tensorflow的内置函数完成。

之后就是打乱数据和划分训练和测试集了。这些代码都是可以直接复用的代码。大部分的深度学习NLP任务都要经过相应的处理。后面我会讲到如何使用训练好的词向量初始化embedding层,它之前的处理和这个也是一样的。这不过,他们使用的词典可能就不是同一个词典了。

train的主体代码

先放上完整的代码,我再逐步分析,相关的分析都在代码注释中体现。

def train(x_train, y_train, vocab_processor, x_dev, y_dev):    # Training    # ==================================================    with tf.Graph().as_default():        session_conf = tf.ConfigProto(          allow_soft_placement=FLAGS.allow_soft_placement,          log_device_placement=FLAGS.log_device_placement)        sess = tf.Session(config=session_conf)        with sess.as_default():            cnn = TextCNN(                sequence_length=x_train.shape[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)            # Define Training procedure            global_step = tf.Variable(0, name="global_step", trainable=False)            optimizer = tf.train.AdamOptimizer(1e-3)            grads_and_vars = optimizer.compute_gradients(cnn.loss)            train_op = optimizer.apply_gradients(grads_and_vars, global_step=global_step)            # Keep track of gradient values and sparsity (optional)            grad_summaries = []            for g, v in grads_and_vars:                if g is not None:                    grad_hist_summary = tf.summary.histogram("{}/grad/hist".format(v.name), g)                    sparsity_summary = tf.summary.scalar("{}/grad/sparsity".format(v.name), tf.nn.zero_fraction(g))                    grad_summaries.append(grad_hist_summary)                    grad_summaries.append(sparsity_summary)            grad_summaries_merged = tf.summary.merge(grad_summaries)            # Output directory for models and summaries            timestamp = str(int(time.time()))            out_dir = os.path.abspath(os.path.join(os.path.curdir, "runs", timestamp))            print("Writing to {}\n".format(out_dir))            # Summaries for loss and accuracy            loss_summary = tf.summary.scalar("loss", cnn.loss)            acc_summary = tf.summary.scalar("accuracy", cnn.accuracy)            # Train Summaries            train_summary_op = tf.summary.merge([loss_summary, acc_summary, grad_summaries_merged])            train_summary_dir = os.path.join(out_dir, "summaries", "train")            train_summary_writer = tf.summary.FileWriter(train_summary_dir, sess.graph)            # Dev summaries            dev_summary_op = tf.summary.merge([loss_summary, acc_summary])            dev_summary_dir = os.path.join(out_dir, "summaries", "dev")            dev_summary_writer = tf.summary.FileWriter(dev_summary_dir, sess.graph)            # Checkpoint directory. Tensorflow assumes this directory already exists so we need to create it            checkpoint_dir = os.path.abspath(os.path.join(out_dir, "checkpoints"))            checkpoint_prefix = os.path.join(checkpoint_dir, "model")            if not os.path.exists(checkpoint_dir):                os.makedirs(checkpoint_dir)            saver = tf.train.Saver(tf.global_variables(), max_to_keep=FLAGS.num_checkpoints)            # Write vocabulary            vocab_processor.save(os.path.join(out_dir, "vocab"))            # Initialize all variables            sess.run(tf.global_variables_initializer())            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)                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)            def dev_step(x_batch, y_batch, writer=None):                """                Evaluates model on a dev set                """                feed_dict = {                  cnn.input_x: x_batch,                  cnn.input_y: y_batch,                  cnn.dropout_keep_prob: 1.0                }                step, summaries, loss, accuracy = sess.run(                    [global_step, dev_summary_op, cnn.loss, cnn.accuracy],                    feed_dict)                time_str = datetime.datetime.now().isoformat()                print("{}: step {}, loss {:g}, acc {:g}".format(time_str, step, loss, accuracy))                if writer:                    writer.add_summary(summaries, step)            # Generate batches            batches = data_helpers.batch_iter(                list(zip(x_train, y_train)), FLAGS.batch_size, FLAGS.num_epochs)            # Training loop. For each batch...            for batch in batches:                x_batch, y_batch = zip(*batch)                train_step(x_batch, y_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)                    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))

CNN模型

主要是重点理解卷积和池化的过程

class TextCNN(object):    """    A CNN for text classification.    Uses an embedding layer, followed by a convolutional, max-pooling and softmax layer.    """    ##初始化函数    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")        self.input_y = tf.placeholder(tf.float32, [None, num_classes], name="input_y")        self.dropout_keep_prob = tf.placeholder(tf.float32, name="dropout_keep_prob")        # Keeping track of l2 regularization loss (optional)        l2_loss = tf.constant(0.0)        # Embedding layer        ##使用cpu做embedding层的初始化比较快        with tf.device('/cpu:0'), tf.name_scope("embedding"):            self.W = tf.Variable(                tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0),                name="W")            self.embedded_chars = tf.nn.embedding_lookup(self.W, self.input_x)            ##增加维度,-1代表的是最后一维,这边主要是维护最后一维的通道数,图像是none×x×y×chanel的            self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1)        # Create a convolution + maxpool layer for each filter size        pooled_outputs = []        for i, filter_size in enumerate(filter_sizes):            with tf.name_scope("conv-maxpool-%s" % filter_size):                # Convolution Layer                filter_shape = [filter_size, embedding_size, 1, num_filters]                ##前两个是卷积的长和宽,第三个是通道数,最后一个就是输出的通道数,其实就是filter的数目                W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1), name="W")                b = tf.Variable(tf.constant(0.1, shape=[num_filters]), name="b")                conv = tf.nn.conv2d(                    self.embedded_chars_expanded,                    W,                    strides=[1, 1, 1, 1],                    padding="VALID",                    name="conv")                # Apply nonlinearity                h = tf.nn.relu(tf.nn.bias_add(conv, b), name="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)        # Combine all the pooled features        num_filters_total = num_filters * len(filter_sizes)        ##以第四维来拼接这个张量        self.h_pool = tf.concat(pooled_outputs, 3)        ##把这个张量压平        self.h_pool_flat = tf.reshape(self.h_pool, [-1, num_filters_total])        # Add dropout        with tf.name_scope("dropout"):            self.h_drop = tf.nn.dropout(self.h_pool_flat, self.dropout_keep_prob)        # Final (unnormalized) scores and predictions        with tf.name_scope("output"):            W = tf.get_variable(                "W",                shape=[num_filters_total, num_classes],                initializer=tf.contrib.layers.xavier_initializer())            b = tf.Variable(tf.constant(0.1, shape=[num_classes]), name="b")            l2_loss += tf.nn.l2_loss(W)            l2_loss += tf.nn.l2_loss(b)            self.scores = tf.nn.xw_plus_b(self.h_drop, W, b, name="scores")            self.predictions = tf.argmax(self.scores, 1, name="predictions")        # Calculate mean cross-entropy loss        with tf.name_scope("loss"):            losses = tf.nn.softmax_cross_entropy_with_logits(logits=self.scores, labels=self.input_y)            self.loss = tf.reduce_mean(losses) + l2_reg_lambda * l2_loss        # Accuracy        with tf.name_scope("accuracy"):            correct_predictions = tf.equal(self.predictions, tf.argmax(self.input_y, 1))            self.accuracy = tf.reduce_mean(tf.cast(correct_predictions, "float"), name="accuracy")






5 个回复

倒序浏览
奈斯
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马