深度學(xué)習Tensorflow2.8?使用?BERT?進行文本分類
前言
本文使用 cpu 版本的 Tensorflow 2.8 ,通過搭建 BERT 模型完成文本分類任務(wù)。
1. python 庫準備
為了保證能正常運行本文代碼,需要保證以下庫的版本:
- tensorflow==2.8.4
- tensorflow-text==2.8.1
- tf-models-official==2.7.0
- python==3.8.0
在安裝 tf-models-official 的時候可能會報錯 :Microsoft Visual C++ 14.0 or greater is required 。直接進入 visualstudio.microsoft.com/zh-hans/vis… 這里進行下載新的Microsoft C++ 生成工具,然后安裝重啟電腦即可。
2. BERT 是什么?
BERT 和其他 Transformer 編碼器架構(gòu)模型都在 NLP 的各種任務(wù)上取得了巨大的成功。它們都是使用了多層的注意力機制,可以有效地對文本進行雙向的深層次語義編碼表示。BERT 模型已經(jīng)在大型文本語料庫上進行了充足的預(yù)訓(xùn)練,我們在使用的時候只需要針對特定任務(wù)進行微調(diào)即可。
3. 獲取并處理 IMDB 數(shù)據(jù)
(1)使用 tensorflow 的內(nèi)置函數(shù),從網(wǎng)絡(luò)上將 Large Movie Review Dataset 數(shù)據(jù)下載到本地,沒有特別指定的話一般位置在當前同級目錄下。此數(shù)據(jù)集是一個電影評論數(shù)據(jù)集,其中包含來自 Internet 電影數(shù)據(jù)庫的 50000 條電影評論的文本,每個文本都對應(yīng)一個標簽標記其為積極或者消極的。
(2)我們將數(shù)據(jù)中無用的 unsup 文件夾都刪掉,這樣后面處理數(shù)據(jù)會更加方便。
import os import shutil import tensorflow as tf import tensorflow_hub as hub import tensorflow_text as text from official.nlp import optimization import matplotlib.pyplot as plt tf.get_logger().setLevel('ERROR') url = 'https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz' dataset = tf.keras.utils.get_file('aclImdb_v1.tar.gz', url, untar=True, cache_dir='.', cache_subdir='') dataset_dir = os.path.join(os.path.dirname(dataset), 'aclImdb') train_dir = os.path.join(dataset_dir, 'train') remove_dir = os.path.join(train_dir, 'unsup') shutil.rmtree(remove_dir)
(3)我們可以直接使用內(nèi)置函數(shù) text_dataset_from_directory 直接從硬盤讀取數(shù)據(jù)生成 tf.data.Dataset 。
(4)IMDB 數(shù)據(jù)集已經(jīng)被分為了訓(xùn)練集和測試集,但是還缺少驗證集,所以讓我們需要從訓(xùn)練集中取出 20% 來創(chuàng)建一個驗證集。最終訓(xùn)練集 20000 個樣本,驗證集 5000 個樣本,測試集 25000 個樣本。每個樣本都是 (text,label) 對。
(5)為了保證在加載數(shù)據(jù)的時候不會出現(xiàn) I/O 不會阻塞,我們在從磁盤加載完數(shù)據(jù)之后,使用 cache 會將數(shù)據(jù)保存在內(nèi)存中,確保在訓(xùn)練模型過程中數(shù)據(jù)的獲取不會成為訓(xùn)練速度的瓶頸。如果說要保存的數(shù)據(jù)量太大,可以使用 cache 創(chuàng)建磁盤緩存提高數(shù)據(jù)的讀取效率。另外我們還使用 prefetch 在訓(xùn)練過程中可以并行執(zhí)行數(shù)據(jù)的預(yù)獲取。
AUTOTUNE = tf.data.AUTOTUNE batch_size = 64 seed = 110 train_datas = tf.keras.utils.text_dataset_from_directory( 'aclImdb/train', batch_size=batch_size, validation_split=0.2, subset='training', seed=seed) class_names = train_datas.class_names train_datas = train_datas.cache().prefetch(buffer_size=AUTOTUNE) val_datas = tf.keras.utils.text_dataset_from_directory( 'aclImdb/train', batch_size=batch_size, validation_split=0.2, subset='validation', seed=seed) val_datas = val_datas.cache().prefetch(buffer_size=AUTOTUNE) test_datas = tf.keras.utils.text_dataset_from_directory( 'aclImdb/test', batch_size=batch_size) test_datas = test_datas.cache().prefetch(buffer_size=AUTOTUNE)
(6)隨機取出兩個處理好的樣本進行展示:
for text_batch, label_batch in train_datas.take(1): for i in range(2): print(f'Review: {text_batch.numpy()[i][:100]}...') label = label_batch.numpy()[i] print(f'Label : {label} ({class_names[label]})')
結(jié)果輸出:
Review: b"This 30 minute documentary Bu\xc3\xb1uel made in the early 1930's about one of Spain's poorest regions is,"...
Label : 0 (neg)
Review: b'I\'ve tried to watch this show several times, but for a show called "That \'70s Show," I don\'t find mu'...
Label : 0 (neg)
4. 初識 TensorFlow Hub 中的 BERT 處理器和模型
(1)由于正規(guī)的從 TensorFlow Hub 下載模型需要“科學(xué)上網(wǎng)”,所以我們可以到這個鏡像網(wǎng)站(hub.tensorflow.google.cn/google/coll… BERT 模型,為了方便我們快速學(xué)習,我們選用了比較小的 Small BERT ,及其對應(yīng)的數(shù)據(jù)輸入處理器。一般下載到本地的路徑為 C:\Users\(用戶名)\AppData\Local\Temp\tfhub_modules\ 下面。
(2)preprocess 可以將文本轉(zhuǎn)化成 BERT 所需要的輸入,這樣就免去了自己寫 Python 代碼來預(yù)處理文本來適應(yīng) BERT 模型的輸入。這里會對文本處理產(chǎn)生對應(yīng)的三個張量 input_word_ids、input_type_ids、input_mask :
- input_word_ids:一個 [batch_size, 128] 的 int32 張量,每個張量包含了每句話中每個 token 對應(yīng)的整數(shù)映射,并且包含了 START、END、PAD 對應(yīng)的整數(shù)符號。如例子所見 how are you 對應(yīng)的 input_word_ids 向量維度為 128 , 101 對應(yīng) START ,102 對應(yīng) END ,中間的數(shù)字對應(yīng)文本中的三個單詞,其余的 0 對應(yīng) PAD 。
- input_mask:一個 [batch_size, 128] 的 int32 張量,PAD 之前的位置,也就是 START、END、以及 token 對應(yīng)的整數(shù)的位置都是用 1 表示,填充 PAD 之后的位置都用 0 表示。如例子所見 how are you 對應(yīng)的 input_mask 向量維度都為 128 ,前 5 個位置都是 1 ,后面全是 0 。
- input_type_ids:一個 [batch_size, 128] 的 int32 張量,如果輸入是分段的,那么第一個輸入段包括 START 和 END 的對應(yīng)位置的都為 0 。如果存在第二段則包括 END 在內(nèi)的輸入都用 1 進行表示, 如果存在第三段則用 2 進行表示,也就是每一段都有一個不同的數(shù)字進行表示,剩下 PAD 填充的位置仍然用 0 表示。如例子所見 how are you 對應(yīng)的 input_type_ids 向量維度為 128 ,前 5 個位置都是 0 ,因為沒有第二段,所以后面都是 PAD 仍然用 0 表示。
(3)同樣我們也使用了 small_bert 接收 preprocess 處理之后的結(jié)果,這時我們可以產(chǎn)生四個對應(yīng)的張量 pooled_output、sequence_output、default、encoder_outputs ,這里我們主要用到前兩個:
- pooled_output:一個 [batch_size, 512] 的 float32 張量,每個張量都是 512 維,表示將每個輸入序列都編碼為一個 512 維的表示向量。
- sequence_output:一個 [batch_size, 128,512] 的 float32 張量,每個張量都是 [128, 512] 維,表示每個輸入序列的每個 token 的編碼結(jié)果輸出是 512 維的表示。
處理器和模型獲?。?/p>
preprocess_url = 'https://hub.tensorflow.google.cn/tensorflow/bert_en_uncased_preprocess/3' preprocess = hub.KerasLayer(preprocess_url) bert_url = 'https://hub.tensorflow.google.cn/tensorflow/small_bert/bert_en_uncased_L-8_H-512_A-8/2' bert_model = hub.KerasLayer(bert_url)
處理器例子展示:
text_test = ['how are you'] preprocess_result = preprocess(text_test) print(f'keys : {list(preprocess_result.keys())}') print(f'shape : {preprocess_result["input_word_ids"].shape}') print(f'input_word_ids : {preprocess_result["input_word_ids"]}') print(f'input_mask : {preprocess_result["input_mask"]}') print(f'input_type_ids : {preprocess_result["input_type_ids"]}')
輸出:
keys : ['input_word_ids', 'input_type_ids', 'input_mask']
shape : (1, 128)
input_word_ids : [[ 101 2129 2024 2017 102 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0]]
input_mask : [[1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]]
input_type_ids : [[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]]
模型例子展示:
bert_results = bert_model(preprocess_result) print(f'Loaded BERT : {bert_url}') print(f'Keys : {list(bert_results.keys())}') print(f'Pooled Outputs Shape :{bert_results["pooled_output"].shape}') print(f'Sequence Outputs Values :{bert_results["pooled_output"].dtype}') print(f'Sequence Outputs Shape :{bert_results["sequence_output"].shape}') print(f'Sequence Outputs Values :{bert_results["sequence_output"].dtype}')
輸出:
Loaded BERT : https://hub.tensorflow.google.cn/tensorflow/small_bert/bert_en_uncased_L-8_H-512_A-8/2
Keys : ['pooled_output', 'sequence_output', 'default', 'encoder_outputs']
Pooled Outputs Shape :(1, 512)
Sequence Outputs Values :<dtype: 'float32'>
Sequence Outputs Shape :(1, 128, 512)
Sequence Outputs Values :<dtype: 'float32'>
5. 搭建模型
(1)第一層是輸入層,用來接收用戶輸入的文本。
(2)第二層是我們上面已經(jīng)介紹過得數(shù)據(jù)處理層,直接用從 TensorFlow Hub 下載的 bert_en_uncased_preprocess 處理器即可。
(3)第三層是我們的 BERT 層,這里也是用我們上面介紹過得模型,直接使用從 TensorFlow Hub 下載的 bert_en_uncased_L-8_H-512_A-8 模型即可。
(4)第四層是一個 Dropout 層,用來將 BERT 輸出進行隨機丟棄,避免過擬合。
(5)第五層一個輸出 1 維向量的全連接層,其實就是輸出該樣本的分類 logit 。
def create_model(): text_input = tf.keras.layers.Input(shape=(), dtype=tf.string, name='text') preprocessing_layer = hub.KerasLayer(preprocess, name='preprocessing') encoder_inputs = preprocessing_layer(text_input) encoder = hub.KerasLayer(bert_url, trainable=True, name='BERT_encoder') outputs = encoder(encoder_inputs) net = outputs['pooled_output'] net = tf.keras.layers.Dropout(0.1)(net) net = tf.keras.layers.Dense(1, activation=None, name='classifier')(net) return tf.keras.Model(text_input, net) model = create_model()
6. 訓(xùn)練模型
(1)由于這是一個二元分類問題,并且模型最終輸出的是概率,因此我們選擇 BinaryCrossentropy 作為損失函數(shù)。使用 BinaryAccuracy 作為我們的評估指標,在進行預(yù)測的時候模型輸出概率大于 threshold 的預(yù)測為 1 也就是積極情緒的,小于等于 threshold 的預(yù)測為 0 ,也就是消極的,threshold 默認是 0.5 。
(2)為了進行微調(diào),我們使用 BERT 最初訓(xùn)練時用的的優(yōu)化器:Adam 。該優(yōu)化器最大程度減少預(yù)測損失,并通過權(quán)重衰減進行正則化,所以它也被稱為 AdamW 。
(3)我們使用與 BERT 預(yù)訓(xùn)練相同的學(xué)習率(也就是我們的 init_lr 變量),訓(xùn)練剛開始時,采用較小的學(xué)習率,隨著迭代次數(shù)增加學(xué)習率線性增大,當?shù)竭_到 num_warmup_steps 時,學(xué)習率設(shè)置為為初始設(shè)定的學(xué)習率 init_lr ,然后學(xué)習率隨著迭代次數(shù)逐步衰減。BERT 論文中將用于微調(diào)的初始學(xué)習率設(shè)置較小,如:5e-5,3e-5,2e-5 。
(4)為什么使用 adamw 優(yōu)化器 ?由于剛開始訓(xùn)練時,模型的權(quán)重是隨機初始化的,此時若選擇一個較大的學(xué)習率,可能帶來模型優(yōu)化的不穩(wěn)定(振蕩),選擇 AdamW 優(yōu)化器,可以使得開始訓(xùn)練的若干 epoches 或者 steps 內(nèi)學(xué)習率較小,在預(yù)熱的小學(xué)習率下,模型可以慢慢趨于穩(wěn)定,等模型相對穩(wěn)定后再選擇預(yù)先設(shè)置的學(xué)習率進行訓(xùn)練(此后的學(xué)習率是衰減的),有助于使模型收斂速度變快,效果更佳。
print(f'Training model with {bert_url}') epochs = 5 steps_per_epoch = tf.data.experimental.cardinality(train_datas).numpy() num_train_steps = steps_per_epoch * epochs num_warmup_steps = int(0.1*num_train_steps) optimizer = optimization.create_optimizer(init_lr=3e-5, num_train_steps=num_train_steps, num_warmup_steps=num_warmup_steps, optimizer_type='adamw') model.compile(optimizer=optimizer, loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), metrics=tf.metrics.BinaryAccuracy()) history = model.fit(x=train_datas, validation_data=val_datas, epochs=epochs)
訓(xùn)練過程,可以看出相當耗時,這也是使用 BERT 的一個明顯缺點:
Training model with https://hub.tensorflow.google.cn/tensorflow/small_bert/bert_en_uncased_L-8_H-512_A-8/2 Epoch 1/5 313/313 [==============================] - 3433s 11s/step - loss: 0.4705 - binary_accuracy: 0.7515 - val_loss: 0.3789 - val_binary_accuracy: 0.8124 Epoch 2/5 313/313 [==============================] - 3328s 11s/step - loss: 0.3043 - binary_accuracy: 0.8653 - val_loss: 0.3734 - val_binary_accuracy: 0.8450 Epoch 3/5 313/313 [==============================] - 3293s 11s/step - loss: 0.2301 - binary_accuracy: 0.9024 - val_loss: 0.4295 - val_binary_accuracy: 0.8532 Epoch 4/5 313/313 [==============================] - 3289s 11s/step - loss: 0.1697 - binary_accuracy: 0.9344 - val_loss: 0.4831 - val_binary_accuracy: 0.8492 Epoch 5/5 313/313 [==============================] - 3411s 11s/step - loss: 0.1308 - binary_accuracy: 0.9497 - val_loss: 0.4631 - val_binary_accuracy: 0.8538
7. 測試模型
使用測試數(shù)據(jù)對訓(xùn)練好的模型進行評估,可以看到準確率達到了 0.8630 ,如果給予充足的調(diào)參和訓(xùn)練時間,效果會更好。
model.evaluate(test_datas)
輸出:
391/391 [==============================] - 1153s 3s/step - loss: 0.4290 - binary_accuracy: 0.8630
8. 保存模型
將訓(xùn)練好的模型保存到本地,以后可以隨時讀取模型進行預(yù)測工作。
dataset_name = 'imdb' saved_model_path = './{}_bert'.format(dataset_name.replace('/', '_')) model.save(saved_model_path, include_optimizer=False)
9. 重新加載模型并進行預(yù)測
我們將使用上面已經(jīng)存在的模型 model 和剛才重新加載的模型 reloaded_model 進行預(yù)測,將一個積極情緒樣本和一個消極情緒樣本輸入模型,發(fā)現(xiàn)能夠預(yù)測正確(接近),而且兩個模型的結(jié)果是一樣的。
def print_my_examples(inputs, results): result_for_printing = [f'input: {inputs[i]:<30} : score: {results[i][0]:.6f}' for i in range(len(inputs))] print(*result_for_printing, sep='\n') examples = ['The movie was great!', 'The movie was terrible...'] reloaded_model = tf.saved_model.load(saved_model_path) reloaded_results = tf.sigmoid(reloaded_model(tf.constant(examples))) original_results = tf.sigmoid(model(tf.constant(examples))) print('Results from reloaded_model:') print_my_examples(examples, reloaded_results) print('Results from model:') print_my_examples(examples, original_results)
結(jié)果輸出:
Results from reloaded_model:
input: The movie was great! : score: 0.994967
input: The movie was terrible... : score: 0.002266
Results from model:
input: The movie was great! : score: 0.994967
input: The movie was terrible... : score: 0.002266
以上就是Tensorflow2.8 使用 BERT 進行文本分類的詳細內(nèi)容,更多關(guān)于Tensorflow BERT文本分類的資料請關(guān)注腳本之家其它相關(guān)文章!
- Tensorflow2.10使用BERT從文本中抽取答案實現(xiàn)詳解
- tensorflow2.10使用BERT實現(xiàn)Semantic Similarity過程解析
- 使用Tensorflow?hub完成目標檢測過程詳解
- 使用TensorFlow創(chuàng)建生成式對抗網(wǎng)絡(luò)GAN案例
- javascript命名約定(變量?函數(shù)?類?組件)
- Tensorflow2.4從頭訓(xùn)練Word?Embedding實現(xiàn)文本分類
- Tensorflow 2.4 搭建單層和多層 Bi-LSTM 模型
- TensorFlow自定義模型保存加載和分布式訓(xùn)練
相關(guān)文章
python定時采集攝像頭圖像上傳ftp服務(wù)器功能實現(xiàn)
本文程序?qū)崿F(xiàn)python定時采集攝像頭圖像上傳ftp服務(wù)器功能,大家參考使用吧2013-12-12解決TensorFlow訓(xùn)練內(nèi)存不斷增長,進程被殺死問題
今天小編就為大家分享一篇解決TensorFlow訓(xùn)練內(nèi)存不斷增長,進程被殺死問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-02-02一文詳解Python如何優(yōu)雅地對數(shù)據(jù)進行分組
這篇文章主要和大家詳細介紹一下Python是如何優(yōu)雅地對數(shù)據(jù)進行分組的,文中通過示例進行了詳細的講解,感興趣的小伙伴可以跟隨小編一起學(xué)習一下2022-07-07python3解析庫BeautifulSoup4的安裝配置與基本用法
簡單來說,BeautifulSoup就是Python的一個HTML或XML的解析庫,我們可以用它來方便地從網(wǎng)頁中提取數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于python3解析庫BeautifulSoup4的安裝配置與基本用法的相關(guān)資料,需要的朋友可以參考下2018-06-06Playwright元素截圖并保存至allure的實現(xiàn)示例
在UI自動化測試中,我們經(jīng)常需要獲取屏幕截圖,本文就介紹一下Playwright元素截圖并保存至allure的實現(xiàn)示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12淺談Python中的函數(shù)(def)及參數(shù)傳遞操作
這篇文章主要介紹了淺談Python中的函數(shù)(def)及參數(shù)傳遞操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-05-05python 機器學(xué)習的標準化、歸一化、正則化、離散化和白化
這篇文章主要介紹了聊聊機器學(xué)習的標準化、歸一化、正則化、離散化和白化,幫助大家更好的理解和學(xué)習使用python進行機器學(xué)習,感興趣的朋友可以了解下2021-04-04