日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

NLP实战-中文新闻文本分类

發布時間:2023/12/20 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NLP实战-中文新闻文本分类 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

1、思路

2、基于paddle的ERINE模型進行遷移學習訓練

3、分步實現

3.1 獲取數據

(1)數據解壓

(2)將文本轉成變量,這里為了好計算,我只選了新聞標題做文本分類

3.2 中文分詞

基于jieba的分詞

基于paddlehub的lac分詞

3.3 創建語料字典,并將語料序列化

基于語料本身的序列化

基于word2vec或已有字典的序列化

3.4 詞嵌入

基于embedding的詞嵌入

基于word2vec的詞嵌入

3.5 構建分類模型

基于LSTM的分類模型:


實現環境:AI studio

1、思路

文本分類任務步驟通常是

  • 文本預處理

文本預處理的方法很多,類似于詞性分析,句法分析,命名實體識別等,在進行文本分類之前,需要將文本進行結構化,常見的方法有one-hot,n_gram,word2vec等,與英文不同(可以簡單用空格和符號進行分詞),中文是比較緊密連接的,結構化之前需要對文本進行分詞,如jieba分詞,此外還需要將分詞之后的語料轉化為ID序列,然后進行詞嵌入(word Embedding)。

  • DL特征提取和分類模型

適合文本的dl?model有RNN,LSTM,GRU等。

  • 預測

訓練完,然后對數據進行預測。

2、基于paddle的ERINE模型進行遷移學習訓練

教程:https://aistudio.baidu.com/aistudio/projectdetail/1735533?forkThirdPart=1

3、分步實現

3.1 獲取數據

(1)數據解壓

!unzip /home/aistudio/data/data8164/THUCNews.zip

解壓之后結果有14個類別,文件夾里邊是新聞文本:

(2)將文本轉成變量,這里為了好計算,我只選了新聞標題做文本分類

(后續覺得數據量太小,就選了全量數據來做,遇到了正則表達式處理慢和分詞慢的情況,推薦FlashText,中英文符號處理)

def read_data(file_dir): z_list = []for parent, dirnames, filenames in os.walk(file_dir):if parent==file_dir:print('父目錄')else:for curDir, dirs, files in os.walk(parent):print("當前文件夾:",curDir)label = curDir.split('/')[-1]title_list = []for i in range(len(files)):fo = open(parent+'/'+files[i],'r',encoding='utf-8')title = fo.readline().strip()title_list.append((title,label))z_list.extend(title_list)return z_list file_dir = '/home/aistudio/THUCNews' data = read_data(file_dir)

3.2 中文分詞

  • 基于jieba的分詞

jieba_c_list = [] #停用詞字典 stopwords = {}.fromkeys([ line.rstrip() for line in open('/home/aistudio/stopwords.txt') ]) for i in range(len(data)):s_t = data[i][0].replace(' ','')s_t = re.sub(r'\d','',s_t)s_t = re.sub(r'\((.*?)\)', '', s_t)s_t = re.sub(r'\,', '', s_t)s_t = re.sub(r'\,', '', s_t)s_t = re.sub(r'\.', '', s_t)s_t = re.sub(r'\%', '', s_t)s_t = re.sub(r'\:', '', s_t)s_t = re.sub(r'\《', '', s_t)s_t = re.sub(r'\》', '', s_t)s_list = jieba.cut(s_t,cut_all=False)final = []for seg in s_list:# seg = seg.encode('gbk')# print(seg)if seg not in stopwords:# print(seg)final.append(seg)jieba_c_list.append((final,data[i][1]))
  • 基于paddlehub的lac分詞

lac = paddlehub.Module(name='lac') lac_list = [] for i in range(len(data)):s_t = data[i][0].replace(' ','')s_t = re.sub(r'\d','',s_t)s_t = re.sub(r'\((.*?)\)', '', s_t)s_t = re.sub(r'\,', '', s_t)s_t = re.sub(r'\,', '', s_t)s_t = re.sub(r'\.', '', s_t)s_t = re.sub(r'\%', '', s_t)s_t = re.sub(r'\:', '', s_t)s_t = re.sub(r'\《', '', s_t)s_t = re.sub(r'\》', '', s_t)results = lac.lexical_analysis(texts=[s_t],batch_size=1)lac_ = results[0]['word']# print(lac_)lac_list.append((lac_,data[i][1]))

3.3 創建語料字典,并將語料序列化

  • 基于語料本身的序列化

(1)構建語料字典

# 構造詞典,統計每個詞的頻率,并根據頻率將每個詞轉換為一個整數id def build_dict(corpus):word_freq_dict = dict()for sentence,_ in corpus:for word in sentence:if word not in word_freq_dict:word_freq_dict[word] = 0word_freq_dict[word] += 1word_freq_dict = sorted(word_freq_dict.items(), key = lambda x:x[1], reverse = True)word2id_dict = dict()word2id_freq = dict()# 一般來說,我們把oov和pad放在詞典前面,給他們一個比較小的id,這樣比較方便記憶,并且易于后續擴展詞表# word2id_dict['[oov]'] = 0# word2id_freq[0] = 1e10# word2id_dict['[pad]'] = 1# word2id_freq[1] = 1e10for word, freq in word_freq_dict:word2id_dict[word] = len(word2id_dict)word2id_freq[word2id_dict[word]] = freqreturn word2id_freq, word2id_dictword2id_freq, word2id_dict = build_dict(jieba_c_list) vocab_size = len(word2id_freq) print("there are totoally %d different words in the corpus" % vocab_size) for _, (word, word_id) in zip(range(10), word2id_dict.items()):print("word %s, its id %d, its word freq %d" % (word, word_id, word2id_freq[word_id]))

(2)ID序列化

label_dict ={'時政':1,'星座':2,'股票':3,'彩票':4,'科技':5,'娛樂':6,'房產':7,'社會':8,'財經':9,'游戲':10,'體育':11,'時尚':12,'家居':13,'教育':14} def convert_corpus_to_id(corpus, word2id_dict):data_set = []for sentence, sentence_label in corpus:# 將句子中的詞逐個替換成id,如果句子中的詞不在詞表內,則替換成oov# 這里需要注意,一般來說我們可能需要查看一下test-set中,句子oov的比例,# 如果存在過多oov的情況,那就說明我們的訓練數據不足或者切分存在巨大偏差,需要調整sentence = [word2id_dict[word] if word in word2id_dict \else word2id_dict['[oov]'] for word in sentence] data_set.append((sentence, label_dict[sentence_label]))return data_settrain_corpus = convert_corpus_to_id(jieba_c_list, word2id_dict) print("%d tokens in the corpus" % len(train_corpus)) print(train_corpus[:5])
  • 基于word2vec或已有字典的序列化

from paddlehub.reader.tokenization import load_vocablabel_dict ={'時政':1,'星座':2,'股票':3,'彩票':4,'科技':5,'娛樂':6,'房產':7,'社會':8,'財經':9,'游戲':10,'體育':11,'時尚':12,'家居':13,'教育':14}# 這是把 中文詞語 轉化為 詞表 中對應 ID 的函數 def convert_tokens_to_ids(vocab, tokens): # 輸入為詞表,和要轉化的 textwids = [] # 初始化一個空的集合,用于存放輸出#tokens = text.split(" ") # 將傳入的 text 用 空格 做分割,變成 詞語字符串 的列表for token in tokens: # 每次從列表里取出一個 詞語wid = vocab.get(token, None)if not wid:wid = vocab["unknown"]wids.append(wid)return widsmodule = paddlehub.Module(name="word2vec_skipgram") # 實例化 word2vec_skipgram 模型vocab = load_vocab(module.get_vocab_path()) # 獲得 詞表 字典# 我們要獲取詞表,直接利用 paddlehub.reader.tokenization 中的 load_vocab 函數即可 # load_vocab 函數的輸入是具體的詞表文件,這里我們用 word2vec_skipgram 附帶的詞表 # 模塊的實例化對象 module 下,用 get_vocab_path() 方法 # 該方法可以在指定的 Module 中(這里就是word2vec_skipgram)查找 assets 文件夾下面有沒有 vocab.txt 文件 # 如果找到,則返回該文件的 具體文件路徑 # load_vocab 函數的返回值是一個 字典,里面 key 為 詞語,value 是詞語對應的 IDtokens_ids = [] for item,_ in lac_list:item_ids = convert_tokens_to_ids(vocab, item) # 獲得組成句子的 詞語 的 ID 列表tokens_ids.append((item_ids,label_dict[_]))for i in range(5):print("token: %s; id: %s" % (lac_list[i], tokens_ids[i]))

3.4 詞嵌入

  • 基于embedding的詞嵌入

embedding在構建神經網絡模型時使用。函數為paddle.nn.Embedding

Embedding(num_embeddings=vocab_size, embedding_dim=hidden_size, sparse=False, weight_attr=paddle.ParamAttr(initializer=paddle.nn.initializer.Uniform(low=-init_scale, high=init_scale)))
  • 基于word2vec的詞嵌入

Word2Vec有GBOW和skip-gram兩種實現方式,實現的庫有gensim、paddlehub,這里用gensim實現。

import gensim #將取字符串,忽略類別(已轉成ID序列) from gensim.models.word2vec import Word2Vecnum_features = 100 # Word vector dimensionality num_workers = 8 # Number of threads to run in paralleltrain_texts = list(map(lambda x: list(x.split()), train_texts)) model = Word2Vec(train_texts, workers=num_workers, size=num_features) model.init_sims(replace=True)# save model model.save("./word2vec.bin") #也可轉換為txt

結果的第一行是(詞數量,詞向量的維度就是num_features)

第二行就是詞向量ID和對應的詞向量

3.5 構建分類模型

基于LSTM的分類模型(此處的class_num改為和新聞類別一致的數量):

from paddle.nn import LSTM, Embedding, Dropout, Linear import paddle.nn.functional as F# 定義一個用于情感分類的網絡實例,SentimentClassifier class SentimentClassifier(paddle.nn.Layer):def __init__(self, hidden_size, vocab_size, class_num=14, num_steps=128, num_layers=1, init_scale=0.1, dropout=None):# 參數含義如下:# 1.hidden_size,表示embedding-size,hidden和cell向量的維度# 2.vocab_size,模型可以考慮的詞表大小# 3.class_num,情感類型個數,可以是2分類,也可以是多分類# 4.num_steps,表示這個情感分析模型最大可以考慮的句子長度# 5.num_layers,表示網絡的層數# 6.init_scale,表示網絡內部的參數的初始化范圍# 長短時記憶網絡內部用了很多Tanh,Sigmoid等激活函數,這些函數對數值精度非常敏感,# 因此我們一般只使用比較小的初始化范圍,以保證效果super(SentimentClassifier, self).__init__()self.hidden_size = hidden_sizeself.vocab_size = vocab_sizeself.class_num = class_numself.init_scale = init_scaleself.num_layers = num_layersself.num_steps = num_stepsself.dropout = dropout# 聲明一個LSTM模型,用來把每個句子抽象成向量self.simple_lstm_rnn = LSTM(input_size=hidden_size, hidden_size=hidden_size, num_layers=num_layers)# 聲明一個embedding層,用來把句子中的每個詞轉換為向量self.embedding = Embedding(num_embeddings=vocab_size, embedding_dim=hidden_size, sparse=False, weight_attr=paddle.ParamAttr(initializer=paddle.nn.initializer.Uniform(low=-init_scale, high=init_scale)))# 在得到一個句子的向量表示后,需要根據這個向量表示對這個句子進行分類# 一般來說,可以把這個句子的向量表示乘以一個大小為[self.hidden_size, self.class_num]的W參數,# 并加上一個大小為[self.class_num]的b參數,從而達到把句子向量映射到分類結果的目的# 我們需要聲明最終在使用句子向量映射到具體情感類別過程中所需要使用的參數# 這個參數的大小一般是[self.hidden_size, self.class_num]self.cls_fc = Linear(in_features=self.hidden_size, out_features=self.class_num, weight_attr=None, bias_attr=None)self.dropout_layer = Dropout(p=self.dropout, mode='upscale_in_train')def forward(self, input, label):# 首先我們需要定義LSTM的初始hidden和cell,這里我們使用0來初始化這個序列的記憶init_hidden_data = np.zeros((self.num_layers, batch_size, embedding_size), dtype='float32')init_cell_data = np.zeros((self.num_layers, batch_size, embedding_size), dtype='float32')# 將這些初始記憶轉換為飛槳可計算的向量# 設置stop_gradient=True,避免這些向量被更新,從而影響訓練效果init_hidden = paddle.to_tensor(init_hidden_data)init_hidden.stop_gradient = Trueinit_cell = paddle.to_tensor(init_cell_data)init_cell.stop_gradient = Trueinit_h = paddle.reshape(init_hidden, shape=[self.num_layers, -1, self.hidden_size])init_c = paddle.reshape(init_cell, shape=[self.num_layers, -1, self.hidden_size])# 將輸入的句子的mini-batch轉換為詞向量表示x_emb = self.embedding(input)x_emb = paddle.reshape(x_emb, shape=[-1, self.num_steps, self.hidden_size])if self.dropout is not None and self.dropout > 0.0:x_emb = self.dropout_layer(x_emb)# 使用LSTM網絡,把每個句子轉換為向量表示rnn_out, (last_hidden, last_cell) = self.simple_lstm_rnn(x_emb, (init_h, init_c))last_hidden = paddle.reshape(last_hidden[-1], shape=[-1, self.hidden_size])# 將每個句子的向量表示映射到具體的情感類別上projection = self.cls_fc(last_hidden)pred = F.softmax(projection, axis=-1)# 根據給定的標簽信息,計算整個網絡的損失函數,這里我們可以直接使用分類任務中常使用的交叉熵來訓練網絡loss = F.softmax_with_cross_entropy(logits=projection, label=label, soft_label=False)loss = paddle.mean(loss)# 最終返回預測結果pred,和網絡的lossreturn pred, loss

?

(5)訓練和預測

# 編寫一個迭代器,每次調用這個迭代器都會返回一個新的batch,用于訓練或者預測 def build_batch(word2id_dict, corpus, batch_size, epoch_num, max_seq_len, shuffle = True, drop_last = True):# 模型將會接受的兩個輸入:# 1. 一個形狀為[batch_size, max_seq_len]的張量,sentence_batch,代表了一個mini-batch的句子。# 2. 一個形狀為[batch_size, 1]的張量,sentence_label_batch,每個元素都是非0即1,代表了每個句子的情感類別(正向或者負向)sentence_batch = []sentence_label_batch = []for _ in range(epoch_num): #每個epoch前都shuffle一下數據,有助于提高模型訓練的效果#但是對于預測任務,不要做數據shuffleif shuffle:random.shuffle(corpus)for sentence, sentence_label in corpus:sentence_sample = sentence[:min(max_seq_len, len(sentence))]if len(sentence_sample) < max_seq_len:for _ in range(max_seq_len - len(sentence_sample)):sentence_sample.append(word2id_dict['[pad]'])sentence_sample = [[word_id] for word_id in sentence_sample]sentence_batch.append(sentence_sample)sentence_label_batch.append([sentence_label])if len(sentence_batch) == batch_size:yield np.array(sentence_batch).astype("int64"), np.array(sentence_label_batch).astype("int64")sentence_batch = []sentence_label_batch = []if not drop_last and len(sentence_batch) > 0:yield np.array(sentence_batch).astype("int64"), np.array(sentence_label_batch).astype("int64")#訓練預測 def train():step = 0sentiment_classifier = SentimentClassifier(embedding_size, vocab_size, num_steps=max_seq_len, num_layers=1)# 創建優化器Optimizer,用于更新這個網絡的參數 optimizer = paddle.optimizer.Adam(learning_rate=0.01, beta1=0.9, beta2=0.999, parameters= sentiment_classifier.parameters()) sentiment_classifier.train()for sentences, labels in build_batch(word2id_dict, train_corpus, batch_size, epoch_num, max_seq_len):sentences_var = paddle.to_tensor(sentences)labels_var = paddle.to_tensor(labels)pred, loss = sentiment_classifier(sentences_var, labels_var)# 后向傳播loss.backward()# 最小化lossoptimizer.step()# 清除梯度optimizer.clear_grad()step += 1if step % 100 == 0:print("step %d, loss %.3f" % (step, loss.numpy()[0]))# 我們希望在網絡訓練結束以后評估一下訓練好的網絡的效果# 通過eval()函數,將網絡設置為eval模式,在eval模式中,網絡不會進行梯度更新eval(sentiment_classifier)def eval(sentiment_classifier):sentiment_classifier.eval()# 這里我們需要記錄模型預測結果的準確率# 對于二分類任務來說,準確率的計算公式為:# (true_positive + true_negative) / # (true_positive + true_negative + false_positive + false_negative)tp = 0.tn = 0.fp = 0.fn = 0.for sentences, labels in build_batch(word2id_dict, test_corpus, batch_size, 1, max_seq_len):sentences_var = paddle.to_tensor(sentences)labels_var = paddle.to_tensor(labels)# 獲取模型對當前batch的輸出結果pred, loss = sentiment_classifier(sentences_var, labels_var)# 把輸出結果轉換為numpy array的數據結構# 遍歷這個數據結構,比較預測結果和對應label之間的關系,并更新tp,tn,fp和fnpred = pred.numpy()for i in range(len(pred)):if labels[i][0] == 1:if pred[i][1] > pred[i][0]:tp += 1else:fn += 1else:if pred[i][1] > pred[i][0]:fp += 1else:tn += 1# 輸出最終評估的模型效果print("the acc in the test set is %.3f" % ((tp + tn) / (tp + tn + fp + fn)))

(全量數據,帶新聞內容的)預測準確率在0.682,考慮優化特征向量或者更改其他模型!

參考資料:

https://blog.csdn.net/qq_42067550/article/details/106101183

https://paddleinference.paddlepaddle.org.cn/product_introduction/inference_intro.html

https://blog.csdn.net/xiaoxiaojie521/article/details/97240436

總結

以上是生活随笔為你收集整理的NLP实战-中文新闻文本分类的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。