微博立場檢測 60分Baseline

  • 2020 年 3 月 17 日
  • 筆記

AI研習社最近舉辦了一個比賽——微博立場檢測,實際上就是一個NLP文本分類的比賽

Baseline—FastText

我的Baseline方法用的是pkuseg分詞+FastText,最好成績是60,下面是我幾次提交的得分截圖

Load Data & Preprocess

先import之後要用到的庫

import pkuseg  import random  import pandas as pd  import fasttext
df = pd.read_csv('train.csv', delimiter='t')

官方給的數據,雖然是csv文件,但是欄位之間用的是t隔開的,所以讀取的時候注意一下就行了。數據樣式如下

stance欄位有三類,分別是FAVORAGAINSTNONE,這也是你需要最終預測的值。但是通過仔細分析數據可以發現,stance欄位除了上面三個值以外還有別的值,所以要先把其它的數據剔除掉

drop_list = []  for i in range(len(df)):      if df.stance[i] != 'FAVOR' and df.stance[i] != 'AGAINST' and df.stance[i] != 'NONE':          drop_list.append(i)  df.drop(drop_list, inplace=True)

FastText讀取的數據應該滿足__lable__xx text,例如

__label__A 我 喜歡 打 籃球  __label__B 我 喜歡 鯤鯤  __label__A 我 喜歡 踢 足球

也就是說,每一行表示一個樣本,並且標籤在前,文本在後,兩者之間用空格隔開。標籤必須以__label__開頭。所以我們要先把原始數據的標籤進行一個轉換,即FAVOR變成__label__AAGAINST變成__label__BNONE變成__label__C

mapping = {'FAVOR':'__label__A', 'AGAINST':'__label__B', 'NONE':'__label__C'}  df['stance'] = df['stance'].map(mapping)

這些都做完以後最好shuffle一下數據

df = df.sample(frac=1)

sample(frac=p)其中$pin[0,1]$,意思是隨機sample出原始數據的百分之多少,如果$p=1$,則表示隨機sample出原始數據的全部,並且由於是隨機sample的,所以原始數據的順序就被打亂了

Split Train & Validation Data

這裡我以7:3的比例將數據集拆分成Train Data和Valid Data

train_len = int(len(df) * 0.7)  df_train = df.loc[:train_len]  df_val = df.loc[train_len:]

Word Segmentation

從FastText讀取數據的樣式可以看出,我們需要對一句話進行分詞。這裡我用的是pkuseg,因為我看它官方API介紹的時候,裡面提到它有一個web語料庫

在分詞前,我先從網上找了一些常見的中英文停用詞

stopwords = []  for line in open('stopwords.txt', encoding='utf-8'):      stopwords.append(line)    stopwords.append('n')  stopwords = set(stopwords)

停用詞表我就不提供了,網上有很多,自己下載即可

然後是一行一行讀取數據並分詞,分完詞再過濾。這些都做完以後,按照FastText要求的格式,拼接字元串,保存到文件中

def dump_file(df, filename, mode='train'):      seg = pkuseg.pkuseg(model_name='web')      with open(filename, 'w',encoding='utf-8') as f:          for i in df.index.values:              segs = seg.cut(df.text[i])              segs = filter(lambda x:x not in stopwords, segs)  #去掉停用詞  #             segs = filter(lambda x:len(x)>1,segs)              segs = filter(lambda x:x.startswith('http')==False, segs)              segs = filter(lambda x:x.startswith('.')==False, segs)              segs = filter(lambda x:x.startswith('-')==False, segs)              segs = filter(lambda x:x.startswith(',')==False, segs)              segs = filter(lambda x:x.startswith('。')==False, segs)              segs = filter(lambda x:x.startswith('…')==False, segs)              segs = filter(lambda x:x.startswith('/')==False, segs)              segs = filter(lambda x:x.startswith('—')==False, segs)              segs = filter(lambda x:x.startswith('、')==False, segs)              segs = filter(lambda x:x.startswith(':')==False, segs)              segs = filter(lambda x:x.startswith('~')==False, segs)              segs = filter(lambda x:x.startswith('[')==False, segs)              segs = filter(lambda x:x.startswith(']')==False, segs)              segs = filter(lambda x:(x.isalpha() and len(x) == 7) == False, segs)              string = ''              for j in segs:                  string = string + ' ' + j              if mode == 'test':                  string = string.lstrip()              else:                  string = df.stance[i] + ' ' + string                  string = string.lstrip()              f.write(string + 'n')
dump_file(df_train, 'train.txt', 'train')  dump_file(df_val, 'val.txt', 'train')

FastText

首先從它官方的github倉庫中clone dev版本(直接使用pip install fasttext是穩定版)

$ git clone https://github.com/facebookresearch/fastText.git  $ cd fastText  $ pip install .

因為最新的dev版本中有一個參數autotuneValidationFile可以在訓練過程中自動搜索使得acc最大的參數。fastText使用也很簡單

clf = fasttext.train_supervised(input='train.txt', autotuneValidationFile='val.txt')

指定訓練集以及用於幫助尋找最優參數的測試集的路徑即可。如果要保存模型就用

clf.save_model('fasttext_model')

Predict & Submit

基本上如果你按照我的方法一路做下來,到現在為止在驗證集上的最大分數也就60左右

然後就是對test集進行預測,預測完了提交就行了

test = pd.read_csv('test.csv', delimiter='t')  dump_file(test, 'test.txt', 'test')    labels = []  for line in open('test.txt', encoding='utf-8'):      if line != '':          line = line.strip('n')          labels.append(clf.predict(line)[0][0])    test['idx'] = range(len(test))  test['stance'] = labels  mapping = {'__label__A':'FAVOR','__label__B':'AGAINST','__label__C':'NONE'}  test['stance'] = test['stance'].map(mapping)  test = test.drop(['target', 'text'], axis=1)    test.to_csv('test_pred.csv',index=False,header=False)

Improve

  1. 我的做法只用了textstance這兩列,target我覺得可以用也可以不用
  2. 仔細觀察數據集會發現,其實樣本分布及其不均勻,stance列中FAVORAGAINST兩個值特別多,NONE特別少,這就涉及到不均衡樣本的訓練問題,可以通過sample,將它們的比例設置的比較均衡了再訓練
  3. 過濾詞設置的更詳細一點。如果你仔細查看了分詞後的數據集,你應該能發現裡面其實還有很多垃圾詞,比方說網址、7位驗證碼、表情之類的
  4. 直接上BERT