本文由**罗周杨stupidme.me.lzy@gmail.com**原创,转载请注明原作者和出处。

原文链接:luozhouyang.github.io/deepseg

分词作为NLP的基础工作之一,对模型的效果有直接的影响。一个效果好的分词,可以让模型的性能更好。

在尝试使用神经网络来分词之前,我使用过jieba分词,以下是一些感受:

  • 分词速度快
  • 词典直接影响分词效果,对于特定领域的文本,词典不足,导致分词效果不尽人意
  • 对于含有较多错别字的文本,分词效果很差

后面两点是其主要的缺点。根据实际效果评估,我发现使用神经网络分词,这两个点都有不错的提升。

本文将带你使用tensorflow实现一个基于BiLSTM+CRF的神经网络中文分词模型。

完整代码已经开源: luozhouyang/deepseg 。

怎么做分词

分词的想法和NER十分接近,区别在于,NER对各种词打上对应的实体标签,而分词对各个字打上位置标签。

目前,项目一共只有以下5中标签:

  • B,处于一个词语的开始
  • M,处于一个词语的中间
  • E,处于一个词语的末尾
  • S,单个字
  • O,未知

举个更加详细的例子,假设我们有一个文本字符串:

'上','海','市','浦','东','新','区','张','东','路','1387','号'

它对应的分词结果应该是:

上海市 浦东新区 张东路 1387 号

所以,它的标签应该是:

'B','M','E','B','M','M','E','B','M','E','S','S'

所以,对于我们的分词模型来说,最重要的任务就是,对于输入序列的每一个token,打上一个标签,然后我们处理得到的标签数据,就可以得到分词效果。

用神经网络给序列打标签,方法肯定还有很多。目前项目使用的是双向LSTM网络后接CRF这样一个网络。这部分会在后面详细说明。

以上就是我们分词的做法概要,如你所见,网络其实很简单。

Estimator

项目使用tensorflow的estimator API完成,因为estimator是一个高级封装,我们只需要专注于核心的工作即可,并且它可以轻松实现分布式训练。如果你还没有尝试过,建议你试一试。

estimator的官方文档可以很好地帮助你入门: estimator

使用estimator构建网络,核心任务是:

  • 构建一个高效的数据输入管道
  • 构建你的神经网络模型

对于数据输入管道,本项目使用tensorflow的Dataset API,这也是官方推荐的方式。

具体来说,给estimator喂数据,需要实现一个input_fn,这个函数不带参数,并且返回(features, labels)元组。当然,对于PREDICT模式,labelsNone

要构建神经网络给estimator,需要实现一个model_fn(features, labels, mode, params, config),返回一个tf.estimator.EstimatorSepc对象。

更多的内容,请访问官方文档。

构建input_fn

首先,我们的数据输入需要分三种模式TRAINEVALPREDICT讨论。

  • TRAIN模式即模型的训练,这个时候使用的是数据集是训练集,需要返回(features,labels)元组
  • EVAL模式即模型的评估,这个时候使用的是数据集的验证集,需要返回(features,labels)元组
  • PREDICT模式即模型的预测,这个时候使用的数据集是测试集,需要返回(features,None)元组

以上的featureslabels可以是任意对象,比如dict,或者是自己定义的python class。实际上,比较推荐使用dict的方式,因为这种方式比较灵活,并且在你需要导出模型到serving的时候,特别有用。这一点会在后面进一步说明。

那么,接下来可以为上面三种模式分别实现我们的inpuf_fn

对于最常见的TRAIN模式:


def build_train_dataset(params):"""Build data for input_fn in training mode.Args:params: A dictReturns:A tuple of (features,labels)."""src_file = params['train_src_file']tag_file = params['train_tag_file']if not os.path.exists(src_file) or not os.path.exists(tag_file):raise ValueError("train_src_file and train_tag_file must be provided")src_dataset = tf.data.TextLineDataset(src_file)tag_dataset = tf.data.TextLineDataset(tag_file)dataset = _build_dataset(src_dataset, tag_dataset, params)iterator = dataset.make_one_shot_iterator()(src, src_len), tag = iterator.get_next()features = {"inputs": src,"inputs_length": src_len}return features, tag

使用tensorflow的Dataset API很简单就可以构建出数据输入管道。首先,根据参数获取训练集文件,分别构建出一个tf.data.TextLineDataset对象,然后构建出数据集。根据数据集的迭代器,获取每一批输入的(features,labels)元组。每一次训练的迭代,这个元组都会送到model_fn的前两个参数(features,labels,...)中。

根据代码可以看到,我们这里的features是一个dict,每一个键都存放着一个Tensor

  • inputs:文本数据构建出来的字符张量,形状是(None,None)
  • inputs_length:文本分词后的长度张量,形状是(None)

而我们的labels就是一个张量,具体是什么呢?需要看一下_build_dataset()函数做了什么:


def _build_dataset(src_dataset, tag_dataset, params):"""Build dataset for training and evaluation mode.Args:src_dataset: A `tf.data.Dataset` objecttag_dataset: A `tf.data.Dataset` objectparams: A dict, storing hyper paramsReturns:A `tf.data.Dataset` object, producing features and labels."""dataset = tf.data.Dataset.zip((src_dataset, tag_dataset))if params['skip_count'] > 0:dataset = dataset.skip(params['skip_count'])if params['shuffle']:dataset = dataset.shuffle(buffer_size=params['buff_size'],seed=params['random_seed'],reshuffle_each_iteration=params['reshuffle_each_iteration'])if params['repeat']:dataset = dataset.repeat(params['repeat']).prefetch(params['buff_size'])dataset = dataset.map(lambda src, tag: (tf.string_split([src], delimiter=",").values,tf.string_split([tag], delimiter=",").values),num_parallel_calls=params['num_parallel_call']).prefetch(params['buff_size'])dataset = dataset.filter(lambda src, tag: tf.logical_and(tf.size(src) > 0, tf.size(tag) > 0))dataset = dataset.filter(lambda src, tag: tf.equal(tf.size(src), tf.size(tag)))if params['max_src_len']:dataset = dataset.map(lambda src, tag: (src[:params['max_src_len']],tag[:params['max_src_len']]),num_parallel_calls=params['num_parallel_call']).prefetch(params['buff_size'])dataset = dataset.map(lambda src, tag: (src, tf.size(src), tag),num_parallel_calls=params['num_parallel_call']).prefetch(params['buff_size'])dataset = dataset.padded_batch(batch_size=params.get('batch_size', 32),padded_shapes=(tf.TensorShape([None]),tf.TensorShape([]),tf.TensorShape([None])),padding_values=(tf.constant(params['pad'], dtype=tf.string),0,tf.constant(params['oov_tag'], dtype=tf.string)))dataset = dataset.map(lambda src, src_len, tag: ((src, src_len), tag),num_parallel_calls=params['num_parallel_call']).prefetch(params['buff_size'])return dataset

虽然代码都很直白,在此还是总结一下以上数据处理的步骤:

  • 跳过和随机打乱数据
  • 根据,将文本序列和对应的标签切分开来
  • 过滤掉空的序列
  • 限制序列的最大长度
  • 增加序列的原始长度信息
  • 对齐和批量

上述过程,最重要的就是padded_batch这一步了。经过之前的处理,现在我们的数据包含以下三项信息:

  • src,原始的字符序列,长度不定
  • src_len,原始字符序列的长度(切分后的列表的长度),长度固定,是一个标量
  • tag,序列对应的标签序列,长度不定

把数据喂入网络之前,我们需要对这些数据进行对齐操作。什么是对齐呢?顾名思义:在这一批数据中,找出最长序列的长度,以此为标准,如果序列比这个长度更短,则文本序列在末尾追加特殊标记(例如<PAD>),标签序列在末尾追加标签的特殊标记(例如O)。因为大家的长度都是不定的,所以要补齐多少个特殊标记也是不定的,所以padded_shapes里面设置成tf.TensorShape([None])即可,函数会自动计算长度的差值,然后进行补齐。

src_len一项是不需要对齐的,因为所有的src_len都是一个scalar。

至此,TRAIN模式下的数据输入准备好了。

EVAL模式下的数据准备和TRAIN模式一模一样,唯一的差别在于使用的数据集不一样,TRAIN模式使用的是训练集,但是EVAL使用的是验证集,所以只需要改一下文件即可。以下是EVAL模式的数据准备过程:


def build_eval_dataset(params):"""Build data for input_fn in evaluation mode.Args:params: A dict.Returns:A tuple of (features, labels)."""src_file = params['eval_src_file']tag_file = params['eval_tag_file']if not os.path.exists(src_file) or not os.path.exists(tag_file):raise ValueError("eval_src_file and eval_tag_file must be provided")src_dataset = tf.data.TextLineDataset(src_file)tag_dataset = tf.data.TextLineDataset(tag_file)dataset = _build_dataset(src_dataset, tag_dataset, params)iterator = dataset.make_one_shot_iterator()(src, src_len), tag = iterator.get_next()features = {"inputs": src,"inputs_length": src_len}return features, tag

至于PREDICT模式,稍微有点特殊,因为要对序列进行预测,我们是没有标签数据的。所以,我们的数据输入只有features这一项,labels这一项只能是None。该模式下的数据准备如下:


def build_predict_dataset(params):"""Build data for input_fn in predict mode.Args:params: A dict.Returns:A tuple of (features, labels), where labels are None."""src_file = params['predict_src_file']if not os.path.exists(src_file):raise FileNotFoundError("File not found: %s" % src_file)dataset = tf.data.TextLineDataset(src_file)if params['skip_count'] > 0:dataset = dataset.skip(params['skip_count'])dataset = dataset.map(lambda src: tf.string_split([src], delimiter=",").values,num_parallel_calls=params['num_parallel_call']).prefetch(params['buff_size'])dataset = dataset.map(lambda src: (src, tf.size(src)),num_parallel_calls=params['num_parallel_call']).prefetch(params['buff_size'])dataset = dataset.padded_batch(params.get('batch_size', 32),padded_shapes=(tf.TensorShape([None]),tf.TensorShape([])),padding_values=(tf.constant(params['pad'], dtype=tf.string),0))iterator = dataset.make_one_shot_iterator()(src, src_len) = iterator.get_next()features = {"inputs": src,"inputs_length": src_len}return features, None

整体的思路差不多,值得注意的是,PREDICT模式的数据不能够打乱数据。同样的进行对齐和分批之后,就可以通过迭代器获取到features数据,然后返回(features,labels)元组,其中labels=None

至此,我们的input_fn就实现了!

值得注意的是,estimator需要的input_fn是一个没有参数的函数,我们这里的input_fn是有参数的,那怎么办呢?用funtiontools转化一下即可,更详细的内容请查看源码。

还有一个很重要的一点,很多项目都会在这个input_fn里面讲字符序列转化成数字序列,但是我们没有这么做,而是依然保持是字符,为什么:

因为这样就可以把这个转化过程放到网络的构建过程中,这样的话,导出模型所需要的serving_input_receiver_fn的构建就会很简单!

这一点详细地说明一下。如果我们把字符数字化放到网络里面去,那么我们导出模型所需要的serving_input_receiver_fn就可以这样写:

def server_input_receiver_fn()receiver_tensors{"inputs": tf.placeholder(dtype=tf.string, shape=(None,None)),"inputs_length": tf.placeholder(dtype=tf.int32, shape=(None))}features = receiver_tensors.copy()return tf.estimator.export.ServingInputReceiver(features=features,receiver_tensors=receiver_tensors)

可以看到,我们在这里也不需要把接收到的字符张量数字化

相反,如果我们在处理数据集的时候进行了字符张量的数字化,那就意味着构建网络的部分没有数字化这个步骤!所有喂给网络的数据都是已经数字化的

这也就意味着,你的serving_input_receiver_fn也需要对字符张量数字化!这样就会使得代码比较复杂!

说了这么多,其实就一点:

  • input_fn里面不要把字符张量转化成数字张量!把这个过程放到网络里面去!

构建神经网络

接下来是最重要的步骤,即构建出我们的神经网络,也就是实现model_fn(features,labels,mode,params,config)这个函数。

首先,我们的参数中的featureslabels都是字符张量,老规矩,我们需要进行word embedding。代码很简单:

words = features['inputs']
nwords = features['inputs_length']
# a UNK token should placed in the first row in vocab file
words_str2idx = lookup_ops.index_table_from_file(params['src_vocab'], default_value=0)
words_ids = words_str2idx.lookup(words)training = mode == tf.estimator.ModeKeys.TRAIN# embedding
with tf.variable_scope("embedding", reuse=tf.AUTO_REUSE):variable = tf.get_variable("words_embedding",shape=(params['vocab_size'], params['embedding_size']),dtype=tf.float32)embedding = tf.nn.embedding_lookup(variable, words_ids)embedding = tf.layers.dropout(embedding, rate=params['dropout'], training=training)

接下来,把词嵌入之后的数据,输入到一个双向LSTM网络:

# BiLSTM
with tf.variable_scope("bilstm", reuse=tf.AUTO_REUSE):# transpose embedding for time major modeinputs = tf.transpose(embedding, perm=[1, 0, 2])lstm_fw = tf.nn.rnn_cell.LSTMCell(params['lstm_size'])lstm_bw = tf.nn.rnn_cell.LSTMCell(params['lstm_size'])(output_fw, output_bw), _ = tf.nn.bidirectional_dynamic_rnn(cell_fw=lstm_fw,cell_bw=lstm_bw,inputs=inputs,sequence_length=nwords,dtype=tf.float32,swap_memory=True,time_major=True)output = tf.concat([output_fw, output_bw], axis=-1)output = tf.transpose(output, perm=[1, 0, 2])output = tf.layers.dropout(output, rate=params['dropout'], training=training)

BiLSTM出来的结果,接入一个CRF层:

logits = tf.layers.dense(output, params['num_tags'])
with tf.variable_scope("crf", reuse=tf.AUTO_REUSE):variable = tf.get_variable("transition",shape=[params['num_tags'], params['num_tags']],dtype=tf.float32)
predict_ids, _ = tf.contrib.crf.crf_decode(logits, variable, nwords)
return logits, predict_ids

返回的logits用来计算loss,更新权重。

损失计算如下:


def compute_loss(self, logits, labels, nwords, params):"""Compute loss.Args:logits: A tensor, output of dense layerlabels: A tensor, the ground truth labelnwords: A tensor, length of inputsparams: A dict, storing hyper paramsReturns:A loss tensor, negative log likelihood loss."""tags_str2idx = lookup_ops.index_table_from_file(params['tag_vocab'], default_value=0)actual_ids = tags_str2idx.lookup(labels)# get transition matrix created beforewith tf.variable_scope("crf", reuse=True):trans_val = tf.get_variable("transition",shape=[params['num_tags'], params['num_tags']],dtype=tf.float32)log_likelihood, _ = tf.contrib.crf.crf_log_likelihood(inputs=logits,tag_indices=actual_ids,sequence_lengths=nwords,transition_params=trans_val)loss = tf.reduce_mean(-log_likelihood)return loss

定义好了损失,我们就可以选择一个优化器来训练我们的网络啦。代码如下:

def build_train_op(self, loss, params):global_step = tf.train.get_or_create_global_step()if params['optimizer'].lower() == 'adam':opt = tf.train.AdamOptimizer()return opt.minimize(loss, global_step=global_step)if params['optimizer'].lower() == 'momentum':opt = tf.train.MomentumOptimizer(learning_rate=params.get('learning_rate', 1.0),momentum=params['momentum'])return opt.minimize(loss, global_step=global_step)if params['optimizer'].lower() == 'adadelta':opt = tf.train.AdadeltaOptimizer()return opt.minimize(loss, global_step=global_step)if params['optimizer'].lower() == 'adagrad':opt = tf.train.AdagradOptimizer(learning_rate=params.get('learning_rate', 1.0))return opt.minimize(loss, global_step=global_step)# TODO(luozhouyang) decay lrsgd = tf.train.GradientDescentOptimizer(learning_rate=params.get('learning_rate', 1.0))return sgd.minimize(loss, global_step=global_step)

当然,你还可以添加一些hooks,比如在EVAL模式下,添加一些统计:

def build_eval_metrics(self, predict_ids, labels, nwords, params):tags_str2idx = lookup_ops.index_table_from_file(params['tag_vocab'], default_value=0)actual_ids = tags_str2idx.lookup(labels)weights = tf.sequence_mask(nwords)metrics = {"accuracy": tf.metrics.accuracy(actual_ids, predict_ids, weights)}return metrics

至此,我们的网络构建完成。完整的model_fn如下:

    def model_fn(self, features, labels, mode, params, config):words = features['inputs']nwords = features['inputs_length']# a UNK token should placed in the first row in vocab filewords_str2idx = lookup_ops.index_table_from_file(params['src_vocab'], default_value=0)words_ids = words_str2idx.lookup(words)training = mode == tf.estimator.ModeKeys.TRAIN# embeddingwith tf.variable_scope("embedding", reuse=tf.AUTO_REUSE):variable = tf.get_variable("words_embedding",shape=(params['vocab_size'], params['embedding_size']),dtype=tf.float32)embedding = tf.nn.embedding_lookup(variable, words_ids)embedding = tf.layers.dropout(embedding, rate=params['dropout'], training=training)# BiLSTMwith tf.variable_scope("bilstm", reuse=tf.AUTO_REUSE):# transpose embedding for time major modeinputs = tf.transpose(embedding, perm=[1, 0, 2])lstm_fw = tf.nn.rnn_cell.LSTMCell(params['lstm_size'])lstm_bw = tf.nn.rnn_cell.LSTMCell(params['lstm_size'])(output_fw, output_bw), _ = tf.nn.bidirectional_dynamic_rnn(cell_fw=lstm_fw,cell_bw=lstm_bw,inputs=inputs,sequence_length=nwords,dtype=tf.float32,swap_memory=True,time_major=True)output = tf.concat([output_fw, output_bw], axis=-1)output = tf.transpose(output, perm=[1, 0, 2])output = tf.layers.dropout(output, rate=params['dropout'], training=training)logits, predict_ids = self.decode(output, nwords, params)# TODO(luozhouyang) Add hooksif mode == tf.estimator.ModeKeys.PREDICT:predictions = self.build_predictions(predict_ids, params)prediction_hooks = []export_outputs = {'export_outputs': tf.estimator.export.PredictOutput(predictions)}return tf.estimator.EstimatorSpec(mode=mode,predictions=predictions,export_outputs=export_outputs,prediction_hooks=prediction_hooks)loss = self.compute_loss(logits, labels, nwords, params)if mode == tf.estimator.ModeKeys.EVAL:metrics = self.build_eval_metrics(predict_ids, labels, nwords, params)eval_hooks = []return tf.estimator.EstimatorSpec(mode=mode,loss=loss,eval_metric_ops=metrics,evaluation_hooks=eval_hooks)if mode == tf.estimator.ModeKeys.TRAIN:train_op = self.build_train_op(loss, params)train_hooks = []return tf.estimator.EstimatorSpec(mode=mode,loss=loss,train_op=train_op,training_hooks=train_hooks)

还是推荐去看源码。

模型的训练、估算、预测和导出

接下来就是训练、估算、预测或者导出模型了。这个过程也很简单,因为使用的是estimator API,所以这些步骤都很简单。

项目中创建了一个Runner类来做这些事情。具体代码请到项目页面。

如果你要训练模型:

python -m deepseg.runner \--params_file=deepseg/example_params.json \--mode=train

或者:

python -m deepseg.runner \--params_file=deepseg/example_params.json \--mode=train_and_eval

如果你要使用训练的模型进行预测:

python -m deepseg.runner \--params_file=deepseg/example_params.json \--mode=predict

如果你想导出训练好的模型,部署到tf serving上面:

python -m deepseg.runner \--params_file=deepseg/example_params.json \--mode=export

以上步骤,所有的参数都在example_params.json文件中,根据需要进行修改即可。

另外,本身的代码也相对简单,如果不满足你的需求,可以直接修改源代码。

根据预测结果得到分词

还有一点点小的提示,模型预测返回的结果是np.ndarray,需要将它转化成字符串数组。代码也很简单,就是用UTF-8去解码bytes而已。

拿预测返回结果的predict_tags为例,你可以这样转换:


def convert_prediction_tags_to_string(prediction_tags):"""Convert np.ndarray prediction_tags of output of prediction to string.Args:prediction_tags: A np.ndarray object, value of prediction['prediction_tags']Returns:A list of string predictions tags"""return " ".join([t.decode('utf8') for t in prediction_tags])

如果你想对文本序列进行分词,目前根据以上处理,你得到了预测的标签序列,那么要得到分词的结果,只需要根据标签结果处理一下原来的文本序列即可:

def segment_by_tag(sequences, tags):"""Segment string sequence by it's tags.Args:sequences: A two dimension source string listtags: A two dimension tag string listReturns:A list of segmented string."""results = []for seq, tag in zip(sequences, tags):if len(seq) != len(tag):raise ValueError("The length of sequence and tags are different!")result = []for i in range(len(tag)):result.append(seq[i])if tag[i] == "E" or tag[i] == "S":result.append(" ")results.append(result)return results

举个具体的例子吧,如果你有一个序列:

sequence = [['上', '海', '市', '浦', '东', '新', '区', '张', '东', '路', '1387', '号'],['上', '海', '市', '浦', '东', '新', '区', '张', '衡', '路', '333', '号']
]

你想对这个序列进行分词处理,那么经过我们的神经网络,你得到以下标签序列:

tags = [['B', 'M', 'E', 'B', 'M', 'M', 'E', 'B', 'M', 'E', 'S', 'S'],['B', 'M', 'E', 'B', 'M', 'M', 'E', 'B', 'M', 'E', 'S', 'S']
]

那么,怎么得到分词结果呢?就是利用上面的segment_by_tag函数即可。

得到的分词结果如下:

上海市 浦东新区 张东路 1387 号
上海市 浦东新区 张衡路 333 号

以上就是所有内容了!

如果你有任何疑问,欢迎和我交流!

联系我

  • 微信: luozhouyang0528
  • 邮箱: stupidme.me.lzy@gmail.com
  • 个人公众号:stupidmedotme

自己动手实现神经网络分词模型相关推荐

  1. 【自己动手写神经网络】小白入门连载(二)--机器人时代必须得有人工神经(不是神经病)...

    2019独角兽企业重金招聘Python工程师标准>>> [自己动手写神经网络]小白入门连载(一) 在上一章中,我们已经介绍了神经网络的基本概念.思想,并提及了有关人工神经元模型的部分 ...

  2. R使用neuralnet包构建神经网络回归模型并与线性回归模型对比实战

    R使用neuralnet包构建神经网络回归模型并与线性回归模型对比实战 目录 R使用neuralnet包构建神经网络回归模型并与线性回归模型对比实战 <

  3. DL之DNN:自定义2层神经网络TwoLayerNet模型(封装为层级结构)利用MNIST数据集进行训练、预测

    DL之DNN:自定义2层神经网络TwoLayerNet模型(封装为层级结构)利用MNIST数据集进行训练.预测 导读           计算图在神经网络算法中的作用.计算图的节点是由局部计算构成的. ...

  4. DL之DNN:自定义2层神经网络TwoLayerNet模型(封装为层级结构)利用MNIST数据集进行训练、GC对比

    DL之DNN:自定义2层神经网络TwoLayerNet模型(封装为层级结构)利用MNIST数据集进行训练.GC对比 导读           神经网络算法封装为层级结构的作用.在神经网络算法中,通过将 ...

  5. DL之DNN:自定义2层神经网络TwoLayerNet模型(计算梯度两种方法)利用MNIST数据集进行训练、预测

    DL之DNN:自定义2层神经网络TwoLayerNet模型(计算梯度两种方法)利用MNIST数据集进行训练.预测 导读 利用python的numpy计算库,进行自定义搭建2层神经网络TwoLayerN ...

  6. 今晚直播 | 北邮博士生纪厚业:异质图神经网络之模型和应用

    「PW Live」是 PaperWeekly 的学术直播间,旨在帮助更多的青年学者宣传其最新科研成果.我们一直认为,单向地输出知识并不是一个最好的方式,而有效地反馈和交流可能会让知识的传播更加有意义, ...

  7. PW Live 直播 | 北邮博士生纪厚业:异质图神经网络之模型和应用

    「PW Live」是 PaperWeekly 的学术直播间,旨在帮助更多的青年学者宣传其最新科研成果.我们一直认为,单向地输出知识并不是一个最好的方式,而有效地反馈和交流可能会让知识的传播更加有意义, ...

  8. TensorFlow2.0(三)--Keras构建神经网络回归模型

    Keras构建神经网络回归模型 1. 前言 1. 导入相应的库 2. 数据导入与处理 2.1 加载数据集 2.2 划分数据集 2.3 数据归一化 3. 模型构建与训练 3.1 神经网络回归模型的构建 ...

  9. TensorFlow2.0(二)--Keras构建神经网络分类模型

    Keras构建分类模型 1. tf.keras简介 2. 利用tf.keras构建神经网络分类模型 2.1 导入相应的库 2.2 数据读取与展示 2.3 数据归一化 2.4 构建模型 2.5 模型的编 ...

  10. vc安装.zip_空间分析:4-1.分词模型hanLP简介与安装

    自然语言处理NLP相对来说,是比较火的方向,分词模型很多,选起来容易眼花缭乱,我最开始使用过结巴分词,简单易用.后来又看到了hanLP,介绍上说,它是用<人民日报>语料库训练的,深得我心, ...

最新文章

  1. 在LoadRunner向远程Linux/Unix执行命令行并收集性能数据
  2. python中文名字叫什么-Python为什么取名为Python,很少人知道
  3. 主流Java数据库连接池分析(C3P0,DBCP,TomcatPool,BoneCP,Druid)
  4. VC++制作DLL具体解释
  5. Python数据清理之数据质量
  6. python入门经典100例-Python3经典100例(Python3入门习题) 含答案 doc版
  7. 【数论】21蓝桥:货物摆放
  8. Jvisualvm监控远程SpringBoot项目
  9. 寻找肇事汽车车牌号C语言,北京交通大学C语言综合程序的设计(黄宇班).doc
  10. 自行搭建 Bitwarden 服务
  11. DOA算法3:Matrix Pencil
  12. 常用分子对接软件简介
  13. 点击按钮弹出单选列表对话框和加载Webview
  14. 主干分支开发模式_源代码主干分支开发四大模式
  15. MP-SPDZ详细介绍
  16. 一台电脑寿命一般几年?
  17. 浏览器网页缩放对页面的影响
  18. 【报告分享】2021年Q2中国新经济创业投资数据分析报告-IT桔子(附下载)
  19. 彻底掌握 Commonjs 和 Es Module
  20. 2016计算机应用能力,2016全国专业技术人员计算机应用能力考试.doc

热门文章

  1. 面试官:Java 反射是什么?我回答不上来!
  2. python打印右对齐_python右对齐的实例方法
  3. 自动按键 回车 变成_【按键大扫荡】驾驶员的“眼”
  4. c语言鸡蛙问题用while,C语言第3章课件.ppt
  5. matlab pca重构,如何反转PCA并从几个主要组成部分重建原始变量?
  6. java获取鼠标点击的坐标_怎么我用Java窗外获取鼠标点击的坐标
  7. FR cpt报表的自动滚屏/滚动编辑
  8. java -jar 指定main,java打包成jar并执行jar包中指定的main方法
  9. celery java_Celery(分布式任务队列) 的使用方法总结
  10. ionic + angular + cordova, 打造专属自己的App!