日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

【NLP】从0梳理1场NLP赛事!

發(fā)布時(shí)間:2025/3/12 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【NLP】从0梳理1场NLP赛事! 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

作者:徐美蘭-lan,華南理工大學(xué)

摘要:這是從0實(shí)踐競(jìng)賽第3篇,數(shù)據(jù)競(jìng)賽對(duì)理論實(shí)踐和增加履歷有益,為了讓初學(xué)者也能入門,本文以全球人工智能技術(shù)大賽預(yù)熱賽-中文預(yù)訓(xùn)練模型賽事為背景,梳理了nlp賽事的完整實(shí)踐流程。

今天以天池中文預(yù)訓(xùn)練模型泛化能力賽事為背景,介紹一種適用入門nlp的Baseline:如何使用bert簡(jiǎn)單、快速地完成多任務(wù)多目標(biāo)分類。具體目錄如下:

0. 賽事背景

大賽名稱:全球人工智能技術(shù)創(chuàng)新大賽【熱身賽二】-?中文預(yù)訓(xùn)練模型泛化能力賽事

大賽地址
https://tianchi.aliyun.com/s/3bd272d942f97725286a8e44f40f3f74(或文末閱讀原文)

大賽類型:自然語(yǔ)言處理、預(yù)訓(xùn)練模型

1. 賽題分析

1.本次賽題為數(shù)據(jù)挖掘類型,通過預(yù)訓(xùn)練模型調(diào)優(yōu)進(jìn)行分類。

2.是一個(gè)典型的多任務(wù)多分類問題。

3.主要應(yīng)用keras_bert,以及pandas、numpy、matplotlib、seabon、sklearn、keras等數(shù)據(jù)挖掘常用庫(kù)或者框架來進(jìn)行數(shù)據(jù)挖掘任務(wù)。

4.賽題禁止人工標(biāo)注;微調(diào)階段不得使用外部數(shù)據(jù);三個(gè)任務(wù)只能共用一個(gè)bert;只能單折訓(xùn)練。

2. 數(shù)據(jù)概況

賽題精選了3個(gè)具有代表性的任務(wù),要求選手提交的模型能夠同時(shí)預(yù)測(cè)每個(gè)任務(wù)對(duì)應(yīng)的標(biāo)簽。數(shù)據(jù)下載地址:https://tianchi.aliyun.com/s/3bd272d942f97725286a8e44f40f3f74

數(shù)據(jù)格式:

任務(wù)1:OCNLI–中文原版自然語(yǔ)言推理,包含3個(gè)類別

ocnli_train.head(3) ''' id content1 content2 label 0 0 一月份跟二月份肯定有一個(gè)月份有. 肯定有一個(gè)月份有 0 1 1 一月份跟二月份肯定有一個(gè)月份有. 一月份有 1 2 2 一月份跟二月份肯定有一個(gè)月份有. 一月二月都沒有 2 ''' len(ocnli_train['label'].unique()) #3

任務(wù)2:OCEMOTION–中文情感分類,包含7個(gè)類別

ocemo_train.head(3) ''' id content label 0 0 '你知道多倫多附近有什么嗎?哈哈有破布耶...真的書上寫的你聽哦...你家那塊破布是世界上最... sadness 1 1 平安夜,圣誕節(jié),都過了,我很難過,和媽媽吵了兩天,以死相逼才終止戰(zhàn)爭(zhēng),現(xiàn)在還處于冷戰(zhàn)中。 sadness 2 2 我只是自私了一點(diǎn),做自己想做的事情! sadness ''' len(ocemo_train['label'].unique()) #7

任務(wù)3:TNEWS–今日頭條新聞標(biāo)題分類,包含15個(gè)類別

times_train.head(3) ''' id content label 0 0 上課時(shí)學(xué)生手機(jī)響個(gè)不停,老師一怒之下把手機(jī)摔了,家長(zhǎng)拿發(fā)票讓老師賠,大家怎么看待這種事? 108 1 1 商贏環(huán)球股份有限公司關(guān)于延期回復(fù)上海證券交易所對(duì)公司2017年年度報(bào)告的事后審核問詢函的公告 104 2 2 通過中介公司買了二手房,首付都付了,現(xiàn)在賣家不想賣了。怎么處理? 106 ''' len(times_train['label'].unique()) #15

3. 代碼實(shí)踐

Step 1:環(huán)境準(zhǔn)備

導(dǎo)入相關(guān)包

import pandas as pd import codecs, gc import numpy as np from sklearn.model_selection import KFold from keras_bert import load_trained_model_from_checkpoint, Tokenizer from keras.metrics import top_k_categorical_accuracy from keras.layers import * from keras.callbacks import * from keras.models import Model import keras.backend as K from keras.optimizers import Adam from keras.utils import to_categorical from sklearn.preprocessing import LabelEncoder

如果在google colab上運(yùn)行代碼,需要先將數(shù)據(jù)上傳至driver上。執(zhí)行以下代碼掛在driver并配置相關(guān)環(huán)境。

from google.colab import drivedrive.mount('/content/drive')''' 路徑說明: ../code #保存代碼 ../data #保存數(shù)據(jù) ../subs #保存數(shù)據(jù) ../chinese_roberta_wwm_large_ext_L-24_H-1024_A-16 #bert路徑 '''pip install keras-bert

Step 2:數(shù)據(jù)讀取

path = "/content/drive/My Drive/天池nlp預(yù)訓(xùn)練/"#將ocnli中content1[0:maxlentext1]+content2作為ocnli任務(wù)的content times_train = pd.read_csv(path + '/data/TNEWS_train1128.csv', sep='\t', header=None, names=('id', 'content', 'label')).astype(str) ocemo_train = pd.read_csv(path + '/data/OCEMOTION_train1128.csv',sep='\t', header=None, names=('id', 'content', 'label')).astype(str) ocnli_train = pd.read_csv(path + '/data/OCNLI_train1128.csv', sep='\t', header=None, names=('id', 'content1', 'content2', 'label')).astype(str) ocnli_train['content'] = ocnli_train['content1'] + ocnli_train['content2']#.apply( lambda x: x[:maxlentext1] )times_testa = pd.read_csv(path + '/data/TNEWS_a.csv', sep='\t', header=None, names=('id', 'content')).astype(str) ocemo_testa = pd.read_csv(path + '/data/OCEMOTION_a.csv',sep='\t', header=None, names=('id', 'content')).astype(str) ocnli_testa = pd.read_csv(path + '/data/OCNLI_a.csv', sep='\t', header=None, names=('id', 'content1', 'content2')).astype(str) ocnli_testa['content'] = ocnli_testa['content1']+ ocnli_testa['content2']#.apply( lambda x: x[:maxlentext1] )

1) 數(shù)據(jù)集合并

分別將三個(gè)任務(wù)的content、label列按行concat在一起作為訓(xùn)練集和標(biāo)簽、測(cè)試集,以此簡(jiǎn)單地將三任務(wù)轉(zhuǎn)化為單任務(wù)。

#合并三個(gè)任務(wù)的訓(xùn)練、測(cè)試數(shù)據(jù) train_df = pd.concat([times_train, ocemo_train, ocnli_train[['id','content', 'label']]], axis=0).copy()testa_df = pd.concat([times_testa, ocemo_testa, ocnli_testa[['id', 'content']]], axis=0).copy()

2)標(biāo)簽編碼

#LabelEncoder處理標(biāo)簽,因?yàn)閎ert輸入的label需要從0開始 encode_label = LabelEncoder() train_df['label'] = encode_label.fit_transform(train_df['label'].apply(str))

3) 數(shù)據(jù)信息查看

train_df.info() ''' <class 'pandas.core.frame.DataFrame'> Int64Index: 147453 entries, 0 to 48777 Data columns (total 3 columns):# Column Non-Null Count Dtype --- ------ -------------- ----- 0 id 147453 non-null object1 content 147453 non-null object2 label 147453 non-null int64 dtypes: int64(1), object(2) memory usage: 4.5+ MB '''

數(shù)據(jù)為id、content、label三列,無子句為空的行。

Step 3: 數(shù)據(jù)分析(EDA)

1) 子句長(zhǎng)度統(tǒng)計(jì)分析

統(tǒng)計(jì)子句長(zhǎng)度主要用于設(shè)置輸入bert的序列長(zhǎng)度。

times_train['content'].str.len().describe(percentiles=[.95, .98, .99])\ ,ocemo_train['content'].str.len().describe(percentiles=[.95, .98, .99])\ ,ocnli_train['content1'].str.len().describe(percentiles=[.95, .98, .99])\ ,ocnli_train['content2'].str.len().describe(percentiles=[.95, .98, .99]) ''' (count 63360.000000mean 22.171086std 7.334206min 2.00000050% 22.00000095% 33.00000098% 37.00000099% 39.000000max 145.000000Name: content, dtype: float64, count 35315.000000mean 48.214328std 84.391942min 3.00000050% 34.00000095% 134.00000098% 138.00000099% 142.000000max 12326.000000Name: content, dtype: float64, count 48778.000000mean 24.174607std 11.515428min 8.00000050% 22.00000095% 46.00000098% 49.00000099% 50.000000max 50.000000Name: content1, dtype: float64, count 48778.000000mean 15.828529std 977.396848min 2.00000050% 10.00000095% 21.00000098% 24.00000099% 27.000000max 215874.000000Name: content2, dtype: float64) '''

從上可以看出,當(dāng)設(shè)置bert序列長(zhǎng)度為142時(shí)即可覆蓋約99%子句的全部?jī)?nèi)容。

2)統(tǒng)計(jì)標(biāo)簽的基本分布信息

train_df['label'].value_counts() / train_df.shape[0] ''' 1 0.113467 0 0.109940 17 0.107397 23 0.084603 21 0.060318 10 0.047771 6 0.041749 4 0.039918 13 0.039036 8 0.033292 3 0.032668 5 0.032268 11 0.029487 19 0.029481 9 0.027690 18 0.027588 12 0.027541 16 0.027460 22 0.027412 15 0.022923 7 0.016853 2 0.008993 24 0.006097 20 0.004001 14 0.002048 Name: label, dtype: float64 '''

由上可以看出,標(biāo)簽占比差距非常大。在拆分訓(xùn)練集與驗(yàn)證集時(shí)如果簡(jiǎn)單地采用隨機(jī)拆分,可能會(huì)導(dǎo)致驗(yàn)證集不存在部分標(biāo)簽的情況。

Step 4: 預(yù)訓(xùn)練模型選擇

1)模型選擇

在眾多nlp預(yù)訓(xùn)練模型中,本文baseline選擇了哈工大與訊飛聯(lián)合發(fā)布的基于全詞遮罩(Whole Word Masking)技術(shù)的中文預(yù)訓(xùn)練模型:RoBERTa-wwm-ext-large。點(diǎn)擊以下鏈接了解更多詳細(xì)信息:

  • 論文地址:https://arxiv.org/abs/1906.08101

  • 開源模型地址:https://github.com/ymcui/Chinese-BERT-wwm

  • 哈工大訊飛聯(lián)合實(shí)驗(yàn)室的項(xiàng)目介紹:https://mp.weixin.qq.com/s/EE6dEhvpKxqnVW_bBAKrnA

2)調(diào)優(yōu)參數(shù)配置

為方便調(diào)優(yōu),在同一代碼塊中配置調(diào)優(yōu)的參數(shù)。

#一些調(diào)優(yōu)參數(shù) er_patience = 2 #early_stopping patience lr_patience = 5 #ReduceLROnPlateau patience max_epochs = 2 #epochs lr_rate = 2e-6#learning rate batch_sz = 4 #batch_size maxlen = 256 #設(shè)置序列長(zhǎng)度為,base模型要保證序列長(zhǎng)度不超過512 lr_factor = 0.85 #ReduceLROnPlateau factor maxlentext1 = 200 #選擇ocnli子句一的長(zhǎng)度 n_folds = 10 #設(shè)置驗(yàn)證集的占比:1/n_folds

Step 5: 模型構(gòu)建

1)切分?jǐn)?shù)據(jù)集(Train,Val)進(jìn)行模型訓(xùn)練、評(píng)價(jià)

采用StratifiedKFold分層抽樣抽取10%的訓(xùn)練數(shù)據(jù)作為驗(yàn)證集。

###采用分層抽樣的方式,從訓(xùn)練集中抽取10%作為驗(yàn)證機(jī) from sklearn.model_selection import StratifiedKFold skf = StratifiedKFold(n_splits=n_folds, shuffle=True, random_state=222)X_trn = pd.DataFrame() X_val = pd.DataFrame()for train_index, test_index in skf.split(train_df.copy(), train_df['label']):X_trn, X_val = train_df.iloc[train_index], train_df.iloc[test_index]break#不能多折訓(xùn)練

采用f1值做為評(píng)價(jià)指標(biāo),當(dāng)評(píng)價(jià)指標(biāo)不在提升時(shí),降低學(xué)習(xí)率。

from keras import backend as Kdef f1(y_true, y_pred):def recall(y_true, y_pred):"""Recall metric.Only computes a batch-wise average of recall.Computes the recall, a metric for multi-label classification ofhow many relevant items are selected."""true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))possible_positives = K.sum(K.round(K.clip(y_true, 0, 1)))recall = true_positives / (possible_positives + K.epsilon())return recalldef precision(y_true, y_pred):"""Precision metric.Only computes a batch-wise average of precision.Computes the precision, a metric for multi-label classification ofhow many selected items are relevant."""true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))predicted_positives = K.sum(K.round(K.clip(y_pred, 0, 1)))precision = true_positives / (predicted_positives + K.epsilon())return precisionprecision = precision(y_true, y_pred)recall = recall(y_true, y_pred)return 2*((precision*recall)/(precision+recall+K.epsilon()))

2)構(gòu)造輸入bert的數(shù)據(jù)格式

#標(biāo)簽類別個(gè)數(shù) n_cls = len( train_df['label'].unique() )#訓(xùn)練數(shù)據(jù)、測(cè)試數(shù)據(jù)和標(biāo)簽轉(zhuǎn)化為模型輸入格式 #訓(xùn)練集每行的content、label轉(zhuǎn)為tuple存入list,再轉(zhuǎn)為numpy array TRN_LIST = [] for data_row in X_trn.iloc[:].itertuples():TRN_LIST.append((data_row.content, to_categorical(data_row.label, n_cls))) TRN_LIST = np.array(TRN_LIST)#驗(yàn)證集每行的content、label轉(zhuǎn)為tuple存入list,再轉(zhuǎn)為numpy array VAL_LIST = [] for data_row in X_val.iloc[:].itertuples():VAL_LIST.append((data_row.content, to_categorical(data_row.label, n_cls))) VAL_LIST = np.array(VAL_LIST)#測(cè)試集每行的content、label轉(zhuǎn)為tuple存入list,再轉(zhuǎn)為numpy array,其中l(wèi)abel全為0 DATA_LIST_TEST = [] for data_row in testa_df.iloc[:].itertuples():DATA_LIST_TEST.append((data_row.content, to_categorical(0, n_cls))) DATA_LIST_TEST = np.array(DATA_LIST_TEST)

3)模型搭建

在bert后接一層Lambda層取出[CLS]對(duì)應(yīng)的向量,再接一層Dense層用于分類輸出。

#bert模型設(shè)置 def build_bert(nclass):global lr_ratebert_model = load_trained_model_from_checkpoint(config_path, checkpoint_path, seq_len=None) #加載預(yù)訓(xùn)練模型for l in bert_model.layers:l.trainable = Truex1_in = Input(shape=(None,))x2_in = Input(shape=(None,))x = bert_model([x1_in, x2_in])x = Lambda(lambda x: x[:, 0])(x) #取出[CLS]對(duì)應(yīng)的向量用來做分類p = Dense(nclass, activation='softmax')(x) #直接dense層softmax輸出model = Model([x1_in, x2_in], p)model.compile(loss='categorical_crossentropy',optimizer=Adam(lr_rate), #選擇優(yōu)化器并設(shè)置學(xué)習(xí)率metrics=['accuracy', f1])print(model.summary())return model

4)模型訓(xùn)練

使用google colab 上的V100卡訓(xùn)練一個(gè)epoch需要約1.5小時(shí),跑兩個(gè)epoch即可。

#模型訓(xùn)練函數(shù) def run_nocv(nfold, trn_data, val_data, data_labels, data_test, n_cls):global er_patienceglobal lr_patienceglobal max_epochsglobal f1metricsglobal lr_factortest_model_pred = np.zeros((len(data_test), n_cls))model = build_bert(n_cls)#下行代碼用于加載保存的權(quán)重繼續(xù)訓(xùn)練#model.load_weights(path + '/subs/model.epoch01_val_loss0.9911_val_acc0.6445_val_f10.6276.hdf5')early_stopping = EarlyStopping(monitor= "val_f1", patience=er_patience) #早停法,防止過擬合 #'val_accuracy'plateau = ReduceLROnPlateau(monitor="val_f1", verbose=1, mode='max', factor=lr_factor, patience=lr_patience) #當(dāng)評(píng)價(jià)指標(biāo)不在提升時(shí),降低學(xué)習(xí)率 checkpoint = ModelCheckpoint(path + "/subs/model.epoch{epoch:02d}_val_loss{val_loss:.4f}_val_acc{val_accuracy:.4f}_val_f1{val_f1:.4f}.hdf5", monitor="val_f1", verbose=2, save_best_only=True, mode='max', save_weights_only=True) #保存val_f1最好的模型權(quán)重#訓(xùn)練跟驗(yàn)證集可shuffle打亂,測(cè)試集不可打亂(否則在生成結(jié)果文件的時(shí)候沒法跟ID對(duì)應(yīng)上)train_D = data_generator(trn_data, shuffle=True)valid_D = data_generator(val_data, shuffle=True)test_D = data_generator(data_test, shuffle=False)#模型訓(xùn)練model.fit_generator(train_D.__iter__(),steps_per_epoch=len(train_D),epochs=max_epochs,validation_data=valid_D.__iter__(),validation_steps=len(valid_D),callbacks=[early_stopping, plateau, checkpoint],)#模型預(yù)測(cè)test_model_pred = model.predict_generator(test_D.__iter__(), steps=len(test_D), verbose=1)train_model_pred = test_model_pred#model.predict(train_D.__iter__(), steps=len(train_D), verbose=1)del modelgc.collect() #清理內(nèi)存K.clear_session() #clear_session就是清除一個(gè)sessionreturn test_model_pred, train_model_pred

調(diào)用上述函數(shù)進(jìn)行訓(xùn)練與預(yù)測(cè)。

cvs = 1 #輸出為numpy array格式的25列概率 test_model_pred, train_model_pred = run_nocv(cvs, TRN_LIST, VAL_LIST, None, DATA_LIST_TEST, n_cls)

5)輸出結(jié)果

#將結(jié)果轉(zhuǎn)為DataFrame格式 preds_tst_df = pd.DataFrame(test_model_pred)#再將range(0,25)做encode_label逆變換作為該DataFrame的列名 preds_col_names = encode_label.inverse_transform( range(0,n_cls) ) preds_tst_df.columns = preds_col_names#從每個(gè)任務(wù)對(duì)應(yīng)的概率標(biāo)簽列中找出最大的概率對(duì)應(yīng)的列名作為預(yù)測(cè)結(jié)果 ''' 如ocnli任務(wù)的預(yù)測(cè)結(jié)果只能為0、1、2,那么從preds_tst_df中選擇0-1-2三列中每行概率最大的列名作為ocnli任務(wù)的測(cè)試集預(yù)測(cè)結(jié)果,其它兩個(gè)任務(wù)依此類推。 ''' times_preds = preds_tst_df.head(times_testa.shape[0])[times_train['label'].unique().tolist()] times_preds = times_preds.eq(times_preds.max(1), axis=0).dot(times_preds.columns)ocemo_preds = preds_tst_df.head(times_testa.shape[0] + ocemo_testa.shape[0]).tail(ocemo_testa.shape[0])[ocemo_train['label'].unique().tolist()] ocemo_preds = ocemo_preds.eq(ocemo_preds.max(1), axis=0).dot(ocemo_preds.columns)ocnli_preds = preds_tst_df.tail(ocnli_testa.shape[0])[ocnli_train['label'].unique().tolist()] ocnli_preds = ocnli_preds.eq(ocnli_preds.max(1), axis=0).dot(ocnli_preds.columns)#輸出任務(wù)tnews的預(yù)測(cè)結(jié)果 times_sub = times_testa[['id']].copy() times_sub['label'] = times_preds.values times_sub.to_json(path + "/subs/tnews_predict.json", orient='records', lines=True) #輸出任務(wù)ocemo的預(yù)測(cè)結(jié)果 ocemo_sub = ocemo_testa[['id']].copy() ocemo_sub['label'] = ocemo_preds.values ocemo_sub.to_json(path + "/subs/ocemotion_predict.json", orient='records', lines=True) #輸出任務(wù)ocnli的預(yù)測(cè)結(jié)果 ocnli_sub = ocnli_testa[['id']].copy() ocnli_sub['label'] = ocnli_preds.values ocnli_sub.to_json(path + "/subs/ocnli_predict.json", orient='records', lines=True)

6)線上評(píng)分

第一階段線上評(píng)分:0.6342。

希望能幫助你完整實(shí)踐一場(chǎng)NLP賽事。

往期精彩回顧適合初學(xué)者入門人工智能的路線及資料下載機(jī)器學(xué)習(xí)及深度學(xué)習(xí)筆記等資料打印機(jī)器學(xué)習(xí)在線手冊(cè)深度學(xué)習(xí)筆記專輯《統(tǒng)計(jì)學(xué)習(xí)方法》的代碼復(fù)現(xiàn)專輯 AI基礎(chǔ)下載機(jī)器學(xué)習(xí)的數(shù)學(xué)基礎(chǔ)專輯 本站知識(shí)星球“黃博的機(jī)器學(xué)習(xí)圈子”(92416895) 本站qq群704220115。 加入微信群請(qǐng)掃碼: 與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的【NLP】从0梳理1场NLP赛事!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。