1. 程式人生 > 其它 >拿來即可用系列——超簡單地構建bert文字分類模型

拿來即可用系列——超簡單地構建bert文字分類模型

技術標籤:拿來即可用系列❤️bert文字分類keras

一、前言

bert作為優秀的預訓練模型,在序列標註、文字分類和文字匹配任務中,即使利用少量的標註資料,就可以取得非常好的結果,因此bert也是目前NLP中最火的預訓練模型。bert根據引數量大小分為base版,large版和xlarge版,同時bert的演進版有albert,robert。其中albert版可以理解為bert版本的mini版,雖然是mini版但是有時候在同一任務,反而表現的更好(可惜的是,我遇到任務都是bert要更勝一籌),而robert是bert的加強版,在bert的基礎上,採用更多更好的預訓練資料,同時加大模型的引數量(我遇到的基本robert不會比bert效果差),還有bert-wwm,xlnet,BERT-wwm-ext等等,我建議如果是打比賽的話,可以優先考慮robert和BERT-wwm-ext模型。

二、程式碼詳解

1、這裡支援幾乎上述所有提到的bert模型,只需要下載後,再指定對應的路徑,如下所示:

#預訓練模型路徑
config_path = 'base_bert/bert_config.json'
checkpoint_path = 'base_bert/bert_model.ckpt'
dict_path = 'base_bert/vocab.txt'

2、生成batch 資料

#data_generator只是一種為了節約記憶體的資料方式
class data_generator:
    def __init__(self, data, batch_size=3, shuffle=True):
        self.data = data
        self.batch_size = batch_size
        self.shuffle = shuffle
        self.steps = len(self.data) // self.batch_size
        if len(self.data) % self.batch_size != 0:
            self.steps += 1
 
    def __len__(self):
        return self.steps
 
    def __iter__(self):
        while True:
            idxs = list(range(len(self.data)))
 
            if self.shuffle:
                np.random.shuffle(idxs)
 
            X1, X2, Y = [], [], []
            for i in idxs:
                d = self.data[i]
                text = d[0][:maxlen]
                x1, x2 = tokenizer.encode(first=text)
                y = d[1]
                X1.append(x1)
                X2.append(x2)
                Y.append([y])
                if len(X1) == self.batch_size or i == idxs[-1]:
                    X1 = seq_padding(X1)
                    X2 = seq_padding(X2)
                    Y = seq_padding(Y)
                    yield [X1, X2], Y[:, 0, :]
                    [X1, X2, Y] = [], [], []

3、構建bert模型,這裡利用keras_bert工具構建,keras_bert 是 CyberZHG 大佬封裝好了Keras版的Bert,順便一提的是,除了keras-bert之外,CyberZHG大佬還封裝了很多有價值的keras模組,比如keras-gpt-2(你可以用像用bert一樣用gpt2模型了)、keras-lr-multiplier(分層設定學習率)、keras-ordered-neurons(就是前不久介紹的ON-LSTM)等等

#bert模型設定
def build_bert(nclass):
    bert_model = load_trained_model_from_checkpoint(config_path, checkpoint_path, seq_len=None)  #載入預訓練模型
 
    for l in bert_model.layers:
        l.trainable = True
 
    x1_in = Input(shape=(None,))
    x2_in = Input(shape=(None,))
 
    x = bert_model([x1_in, x2_in])
    x = Lambda(lambda x: x[:, 0])(x) # 取出[CLS]對應的向量用來做分類
    p = Dense(nclass, activation='softmax')(x)
 
    model = Model([x1_in, x2_in], p)
    model.compile(loss='categorical_crossentropy',
                  optimizer=Adam(1e-5),    #用足夠小的學習率
                  metrics=['accuracy', acc_top2])
    print(model.summary())
    return model

4、交叉驗證,訓練並預測

#交叉驗證訓練和測試模型
def run_cv(nfold, data, data_labels, data_test):
    kf = KFold(n_splits=nfold, shuffle=True, random_state=520).split(data)
    train_model_pred = np.zeros((len(data), 3))
    test_model_pred = np.zeros((len(data_test), 3))
 
    for i, (train_fold, test_fold) in enumerate(kf):
        X_train, X_valid, = data[train_fold, :], data[test_fold, :]
 
        model = build_bert(3)
        early_stopping = EarlyStopping(monitor='val_acc', patience=3)   #早停法,防止過擬合
        plateau = ReduceLROnPlateau(monitor="val_acc", verbose=1, mode='max', factor=0.5, patience=2) #當評價指標不在提升時,減少學習率
        checkpoint = ModelCheckpoint('./bert_dump/' + str(i) + '.hdf5', monitor='val_acc',verbose=2, save_best_only=True, mode='max', save_weights_only=True) #儲存最好的模型
 
        train_D = data_generator(X_train, shuffle=True)
        valid_D = data_generator(X_valid, shuffle=True)
        test_D = data_generator(data_test, shuffle=False)
        #模型訓練
        model.fit_generator(
            train_D.__iter__(),
            steps_per_epoch=len(train_D),
            epochs=5,
            validation_data=valid_D.__iter__(),
            validation_steps=len(valid_D),
            callbacks=[early_stopping, plateau, checkpoint],
        )
 
        # model.load_weights('./bert_dump/' + str(i) + '.hdf5')
 
        # return model
        train_model_pred[test_fold, :] = model.predict_generator(valid_D.__iter__(), steps=len(valid_D), verbose=1)
        test_model_pred += model.predict_generator(test_D.__iter__(), steps=len(test_D), verbose=1)
 
        del model
        gc.collect()   #清理記憶體
        K.clear_session()   #clear_session就是清除一個session
        # break
 
    return train_model_pred, test_model_pred

5、完結,撒花,完整程式碼下載:https://github.com/ttjjlw/NLP/tree/main/Classify%E5%88%86%E7%B1%BB/bert/keras_bert

如遇問題,可私信我