實體識別的領域是建筑領域,處理的文本是《地鐵設計規范》,2014 年 3 月 1 日開始施行的。
參考的基準代碼是https://github.com/Determined22/zh-NER-TF,該源碼設計的模型,參考的論文是這兩篇,Bidirectional LSTM-CRF Models for Sequence Tagging
和Neural Architectures for Named Entity Recognition
(注:但是目前寫這一篇時,尚未將代碼和論文模型進行比對)
現將所更改的一些內容,進行記錄。
1.因為我的實體類別粗粒度分為6類,細粒度分為18類,因此默認寫死的tag2lable顯得不太靈活
tag2label.png
并且對應于字編號,我將這一變量更名為tag2id,同時因為字編號的使用方式是寫入文件,由使用方進行讀取使用,所以tag2id也寫入文件,分開兩個文件寫(TODO:可以寫入同一個文件,參考ChineseNER這個代碼的寫法)
代碼位置:data.py/vocab_build
def vocab_build(vocab_dir, corpus_path, min_count):
"""
:param vocab_dir:
:param corpus_path:
:param min_count:
:return:
"""
data = read_corpus(corpus_path)
# word2id篩選掉不滿足字頻的字,將其他字進行編號,并把英文用<ENG>,數字用<NUM>,然后再給未來沒有在字典中的字留一個<UNK>
# 用<UNK>來代替,把不滿足統一句子長度的句子用<PAD>進行填充,形成這樣子的一個字典;其中<PAD>編號為0,<UNK>為最大編號
# word2id = {'<PAD>': 0, '': , '': , .. '<ENG>': , '': , .. '<NUM>': , '': , .. '<UNK>': }
word2id = {}
tag2id = {}
tag_id = 0
for sent_, tag_ in data:
for word in sent_:
if word.isdigit():
word = '<NUM>'
# A-Z, a-z
elif ('\u0041' <= word <='\u005a') or ('\u0061' <= word <='\u007a'):
word = '<ENG>'
if word not in word2id:
word2id[word] = [len(word2id)+1, 1]
else:
word2id[word][1] += 1
for tag in tag_:
if tag not in tag2id:
tag2id[tag] = tag_id
tag_id += 1
low_freq_words = []
for word, [word_id, word_freq] in word2id.items(): # 其實這個word_id真的沒用到,表示字加入字典時的序號
if word_freq < min_count and word != '<NUM>' and word != '<ENG>':
low_freq_words.append(word)
for word in low_freq_words:
del word2id[word]
# 后面修改一下按照字頻去編號,這個也是參考https://github.com/zjy-ucas/ChineseNER
new_id = 1
for word in word2id.keys():
word2id[word] = new_id
new_id += 1
word2id['<UNK>'] = new_id # 查找表中沒有對應的自嵌入,被替換成UNK,使用UNK對應的嵌入
word2id['<PAD>'] = 0 # 為了保證每個batch句子長度一致,用PAD對應嵌入填充句子
# print(len(word2id))
word_vocab_path = os.path.join(vocab_dir, 'word2id.pkl')
with open(word_vocab_path, 'wb') as fw:
pickle.dump(word2id, fw) # 將對象寫入打開的文件中,二進制;反序列化load()
tag_vocab_path = os.path.join(vocab_dir, 'tag2id.pkl')
with open(tag_vocab_path, 'wb') as fw:
pickle.dump(tag2id, fw)
2.適應tag2id和word2id的使用方式
原有main.py中,只讀取word2id,因為tag2id是寫死的,如下圖
讀取word2id.png
改為
i讀取word2id和tag2id.png
對應這個參數是文件夾,因此,將data.py/read_dictionary修改如下
def read_dictionary(vocab_dir):
"""
讀取字典文件
:param vocab_path:
:return:
"""
word_vocab_path = os.path.join(vocab_dir, 'word2id.pkl')
with open(word_vocab_path, 'rb') as fr:
word2id = pickle.load(fr)
print('vocab_size:', len(word2id))
tag_vocab_path = os.path.join(vocab_dir, 'tag2id.pkl')
with open(tag_vocab_path, 'rb') as fr:
tag2id = pickle.load(fr)
print('vocab_size:', len(tag2id))
return word2id, tag2id
3.在這個預處理得到字編號的過程中,將不滿足字頻的字進行了刪除,具體邏輯在data.py/vocab_build,字頻限制使用min_count這個變量,對了,在開始訓練前,需要先得到字編號和標簽編號,因此首先需要在data.py中,我首先設置的詞頻是3,這個后面也可以作為要調的一個參數
if __name__ == '__main__':
'''
vocab_dir = 'data_path/rail_data18'
corpus_path = 'data_path/rail_data18/train_data'
'''
vocab_dir = 'data_path/rail_data6'
corpus_path = 'data_path/rail_data6/train_data'
min_count = 3
vocab_build(vocab_dir, corpus_path, min_count)
word2id, tag2id = read_dictionary(vocab_dir)
print(word2id)
print(tag2id)
4.將結果寫入部分,進行了改動,源碼中是unicode了,改為不進行編碼
not encode.png
5.main.py中將訓練數據的默認路徑進行修改,如圖
數據文件默認位置.png
源代碼中是將測試數據用作驗證用,但我有構造出驗證數據,因此使用驗證數據進行驗證
dev_test1.png
dev_test2.png
6.添加perl語言的解析環境
windows
activeperl官網下載安裝,命令行perl -v檢查安裝成功,但是需要重啟電腦才能被識別到
linux
檢查是否已經安裝:perl -v,沒有直接用rpm安裝就可以了
7.將數據集放入相應的位置,就可以跑起來代碼了
訓練命令:
python main.py --mode=train
測試命令:
python main.py --mode=test --demo_model=1608281271
(后面那個數字為訓練的模型)