事前トレーニングされた BERT モデルを使用してテキスト分類とキーワード抽出を解決します

1. 事前トレーニングされた BERT モデルを使用してテキスト分類問題を解決する

1.関連パッケージをインポートする

import os
import pandas as pd
import torch
from torch import nn
from torch.utils.data import Dataset, DataLoader
# 用于加载bert模型的分词器
from transformers import AutoTokenizer
# 用于加载bert模型
from transformers import BertModel
from pathlib import Path

2. 関連するパラメータを定義する

batch_size = 16
# 文本的最大长度
text_max_length = 128
# 总训练的epochs数,我只是随便定义了个数
epochs = 100
# 学习率
lr = 3e-5
# 取多少训练集的数据作为验证集
validation_ratio = 0.1
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# 每多少步,打印一次loss
log_per_step = 50

# 数据集所在位置
dataset_dir = Path("./基于论文摘要的文本分类与关键词抽取挑战赛公开数据")
os.makedirs(dataset_dir) if not os.path.exists(dataset_dir) else ''

# 模型存储路径
model_dir = Path("./model/bert_checkpoints")
# 如果模型目录不存在,则创建一个
os.makedirs(model_dir) if not os.path.exists(model_dir) else ''

print("Device:", device)

3. データの読み取りとデータの前処理を実行します。

テキスト分類にはモデルをトレーニングするためのデータが必要であるため、最初にデータセットが読み取られて前処理されます。ここではトレーニングセットと検証セットのCSVファイルを読み込み、主に以下の処理を行いました。

  • 空の値を入力します
  • タイトル、著者、要約、その他のフィールドを結合してテキスト コンテンツを取得します
  • トレーニング セットからいくつかのデータを検証セットとしてサンプリングする

まず現在のパスを確認します
%pwd

次に、データセットを読み取り、データ処理を実行します。

pd_train_data = pd.read_csv('../../原始数据/train.csv')
pd_train_data['title'] = pd_train_data['title'].fillna('')
pd_train_data['abstract'] = pd_train_data['abstract'].fillna('')

test_data = pd.read_csv('../../原始数据/testB.csv')
test_data['title'] = test_data['title'].fillna('')
test_data['abstract'] = test_data['abstract'].fillna('')
pd_train_data['text'] = pd_train_data['title'].fillna('') + ' ' +  pd_train_data['author'].fillna('') + ' ' + pd_train_data['abstract'].fillna('')+ ' ' + pd_train_data['Keywords'].fillna('')
test_data['text'] = test_data['title'].fillna('') + ' ' +  test_data['author'].fillna('') + ' ' + test_data['abstract'].fillna('')+ ' ' + pd_train_data['Keywords'].fillna('')

トレーニング セットからテスト セットをランダムにサンプリングする

validation_data = pd_train_data.sample(frac=validation_ratio)
train_data = pd_train_data[~pd_train_data.index.isin(validation_data.index)]

4. トレーニングに必要なデータローダーとデータセットを構築する

4.1 カスタマイズされたデータセットクラス

テキストは BERT モデルに直接入力できないため、最初にトークナイザーを介して ID シーケンスに変換する必要があります。ここでは AutoTokenizer をロードしてカスタム Dataset クラスを構築し、主に以下を完成させました。

  • トークナイザーの単語の分割、切り捨て、パディング
  • __getitem__ メソッドと __len__ メソッドを定義して、後続のバッチ生成を容易にします (pytorch フレームワークで Dataset クラスを記述する標準的な方法)。
# 构建Dataset
class MyDataset(Dataset):

    def __init__(self, mode='train'):
        super(MyDataset, self).__init__()
        self.mode = mode
        # 拿到对应的数据
        if mode == 'train':
            self.dataset = train_data
        elif mode == 'validation':
            self.dataset = validation_data
        elif mode == 'test':
            # 如果是测试模式,则返回内容和uuid。拿uuid做target主要是方便后面写入结果。
            self.dataset = test_data
        else:
            raise Exception("Unknown mode {}".format(mode))

    def __getitem__(self, index):
        # 取第index条
        data = self.dataset.iloc[index]
        # 取其内容
        text = data['text']
        # 根据状态返回内容
        if self.mode == 'test':
            # 如果是test,将uuid做为target
            label = data['uuid']
        else:
            label = data['label']
        # 返回内容和label
        return text, label

    def __len__(self):
        return len(self.dataset)

```python
train_dataset = MyDataset('train')
validation_dataset = MyDataset('validation')

__getitem__ 関数を使用してインデックス 0 のデータを返し、その内容を確認します。
train_dataset.__getitem__(0)

出力:
('Accessible Visual Artworks for Blind and Visually Impaired People: Comparing a Multimodal Approach with Tactile Graphics Quero, Luis Cavazos; Bartolome, Jorge Iranzo; Cho, Jundong Despite the use of tactile graphics and audio guides, blind and visually impaired people still face challenges to experience and understand visual artworks independently at art exhibitions. Art museums and other art places are increasingly exploring the use of interactive guides to make their collections more accessible. In this work, we describe our approach to an interactive multimodal guide prototype that uses audio and tactile modalities to improve the autonomous access to information and experience of visual artworks. The prototype is composed of a touch-sensitive 2.5D artwork relief model that can be freely explored by touch. Users can access localized verbal descriptions and audio by performing touch gestures on the surface while listening to themed background music along. We present the design requirements derived from a formative study realized with the help of eight blind and visually impaired participants, art museum and gallery staff, and artists. We extended the formative study by organizing two accessible art exhibitions. There, eighteen participants evaluated and compared multimodal and tactile graphic accessible exhibits. Results from a usability survey indicate that our multimodal approach is simple, easy to use, and improves confidence and independence when exploring visual artworks. accessibility technology; multimodal interaction; auditory interface; touch interface; vision impairment', 0)

出力にテキストとラベルが含まれていることがわかります。

Bert の事前トレーニング済みモデルを取得する
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")

4.2 データローダーの構築

#接着构造我们的Dataloader。
#我们需要定义一下collate_fn,在其中完成对句子进行编码、填充、组装batch等动作:
def collate_fn(batch):
    """
    将一个batch的文本句子转成tensor,并组成batch。
    :param batch: 一个batch的句子,例如: [('推文', target), ('推文', target), ...]
    :return: 处理后的结果,例如:
             src: {'input_ids': tensor([[ 101, ..., 102, 0, 0, ...], ...]), 'attention_mask': tensor([[1, ..., 1, 0, ...], ...])}
             target:[1, 1, 0, ...]
    """
    text, label = zip(*batch)
    text, label = list(text), list(label)

    # src是要送给bert的,所以不需要特殊处理,直接用tokenizer的结果即可
    # padding='max_length' 不够长度的进行填充
    # truncation=True 长度过长的进行裁剪
    src = tokenizer(text, padding='max_length', max_length=text_max_length, return_tensors='pt', truncation=True)

    return src, torch.LongTensor(label)

トレーニング セットと検証セットのデータローダーを構築し、出力を印刷してローダー内の各サンプルの内容を確認します。

train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, collate_fn=collate_fn)
validation_loader = DataLoader(validation_dataset, batch_size=batch_size, shuffle=False, collate_fn=collate_fn)

inputs, targets = next(iter(train_loader))
print("inputs:", inputs)
print("targets:", targets)

5. モデルの定義

事前トレーニングされた BERT モデルをロードし、BERT エンコーダーや線形レイヤーなどの新しい分類ヘッドを含むニューラル ネットワークを構築して、テキスト特徴の抽出と分類を完了します。

#定义预测模型,该模型由bert模型加上最后的预测层组成
class MyModel(nn.Module):

    def __init__(self):
        super(MyModel, self).__init__()

        # 加载bert模型
        self.bert = BertModel.from_pretrained('bert-base-uncased', mirror='tuna')

        # 最后的预测层
        self.predictor = nn.Sequential(
            nn.Linear(768, 256),
            nn.ReLU(),
            nn.Linear(256, 1),
            nn.Sigmoid()
        )

    def forward(self, src):
        """
        :param src: 分词后的推文数据
        """

        # 将src直接序列解包传入bert,因为bert和tokenizer是一套的,所以可以这么做。
        # 得到encoder的输出,用最前面[CLS]的输出作为最终线性层的输入
        outputs = self.bert(**src).last_hidden_state[:, 0, :]

        # 使用线性层来做最终的预测
        return self.predictor(outputs)
  
model = MyModel()
model = model.to(device)

6. 損失関数とオプティマイザを定義する

Adam オプティマイザー、クロスエントロピー損失関数など、トレーニング プロセスのオプティマイザー、損失関数、戦略などを指定します。トレーニング ループでは、モデルのトレーニング、検証、および最適なモデルの保存が完了します。

#定义出损失函数和优化器。这里使用Binary Cross Entropy:
criteria = nn.BCELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=lr)

入力は辞書型なので、(デバイス)を支援する補助関数を定義します。

def to_device(dict_tensors):
    result_tensors = {
    
    }
    for key, value in dict_tensors.items():
        result_tensors[key] = value.to(device)
    return result_tensors

7. 検証方法を定義する

検証セットの精度と損失を取得するための検証メソッドを定義します。

def validate():
    model.eval()
    total_loss = 0.
    total_correct = 0
    for inputs, targets in validation_loader:
        inputs, targets = to_device(inputs), targets.to(device)
        outputs = model(inputs)
        loss = criteria(outputs.view(-1), targets.float())
        total_loss += float(loss)

        correct_num = (((outputs >= 0.5).float() * 1).flatten() == targets).sum()
        total_correct += correct_num

    return total_correct / len(validation_dataset), total_loss / len(validation_dataset)

8. モデルのトレーニング

# 首先将模型调成训练模式
model.train()

# 清空一下cuda缓存
if torch.cuda.is_available():
    torch.cuda.empty_cache()

# 定义几个变量,帮助打印loss
total_loss = 0.
# 记录步数
step = 0

# 记录在验证集上最好的准确率
best_accuracy = 0

# 开始训练
for epoch in range(epochs):
    model.train()
    for i, (inputs, targets) in enumerate(train_loader):
        # 从batch中拿到训练数据
        inputs, targets = to_device(inputs), targets.to(device)
        # 传入模型进行前向传递
        outputs = model(inputs)
        # 计算损失
        loss = criteria(outputs.view(-1), targets.float())
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()

        total_loss += float(loss)
        step += 1

        if step % log_per_step == 0:
            print("Epoch {}/{}, Step: {}/{}, total loss:{:.4f}".format(epoch+1, epochs, i, len(train_loader), total_loss))
            total_loss = 0

        del inputs, targets

    # 一个epoch后,使用过验证集进行验证
    accuracy, validation_loss = validate()
    print("Epoch {}, accuracy: {:.4f}, validation loss: {:.4f}".format(epoch+1, accuracy, validation_loss))
    #torch.save(model, model_dir / f"model_{epoch}.pt")

    # 保存最好的模型
    if accuracy > best_accuracy:
        torch.save(model, model_dir / f"model_best.pt")
        best_accuracy = accuracy

9. 予測

最適なモデルをロードし、テスト セットで予測を行う

model = torch.load(model_dir / f"model_best.pt")
model = model.eval()

10.結果の出力

test_dataset = MyDataset('test')
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, collate_fn=collate_fn)

results = []
for inputs, ids in test_loader:
    outputs = model(inputs.to(device))
    outputs = (outputs >= 0.5).int().flatten().tolist()
    ids = ids.tolist()
    results = results + [(id, result) for result, id in zip(outputs, ids)]

test_label = [pair[1] for pair in results]
test_data['label'] = test_label
test_data[['uuid', 'label']].to_csv('submit_task1.csv', index=None)

このとき、タスク 1 のラベルの予測結果が submit_task1.csv ファイルに保存されます。

モデル分類の予測結果を CSV ファイルとして保存して提出します。

このようにして、テキスト分類に BERT を使用する主なプロセスが紹介されました。この事前トレーニング微調整方法は、BERT の強力な意味表現機能を組み合わせており、テキスト分類の効果を大幅に向上させることができます。将来的には、モデルの効果をさらに調査するために、モデル構造、スーパーパラメーターなどをさらに最適化することができます。

2. 事前トレーニングされた BERT モデルを使用してキーワード抽出を解決する

特定の文書から重要な情報を理解したい場合、通常はキーワードの抽出に頼ります。キーワード抽出は、入力テキストに最も関連性の高い単語や語句を抽出する自動プロセスです。
KeyBERT に似た統合ツール ライブラリはすでに数多くあり、非常に優れた結果を達成できますが、ここでは BERT を使用して独自のキーワード抽出モデルを作成し、誰もがテキスト キーワード抽出のプロセス全体をより効率的に完了できるようにすることにしました。

1. 関連ライブラリをインポートする


# 导入pandas用于读取表格数据
import pandas as pd

# 导入BOW(词袋模型),可以选择将CountVectorizer替换为TfidfVectorizer(TF-IDF(词频-逆文档频率)),注意上下文要同时修改,亲测后者效果更佳
from sklearn.feature_extraction.text import TfidfVectorizer
# 导入Bert模型
from sentence_transformers import SentenceTransformer

# 导入计算相似度前置库,为了计算候选者和文档之间的相似度,我们将使用向量之间的余弦相似度,因为它在高维度下表现得相当好。
from sklearn.metrics.pairwise import cosine_similarity

# 过滤警告消息
from warnings import simplefilter
from sklearn.exceptions import ConvergenceWarning
simplefilter("ignore", category=ConvergenceWarning)

2. データセットを読み取り、処理します。

ここで B リストのテスト セットを使用しているのは、7 月 24 日に B リストが公開された後、タスク 1 とタスク 2 が 1 つに統合されたためです。以前のテスト セットを使用してラベルを予測するだけでは、最終提出には意味がありません。

# 读取数据集
test = pd.read_csv('../../原始数据/testB.csv')
test['title'] = test['title'].fillna('')
test['abstract'] = test['abstract'].fillna('')

test['text'] = test['title'].fillna('') + ' ' +test['abstract'].fillna('')

ストップワード フィルタリングは、キーワードの品質を向上させる重要な手段です。実際の意味を持たない単語を除外するためのストップワード リストを提供します。フィルタリング効果を高めるために、テキストフィールドに応じてストップワードリストを調整することをお勧めします。

# 定义停用词,去掉出现较多,但对文章不关键的词语
stops =[i.strip() for i in open(r'./stop.txt',encoding='utf-8').readlines()] 

3. BERT モデルをロードする

ここでは、distiluse-base-multilingual-cases の事前トレーニング済み BERT モデルを選択しました。これは多言語 BERT の精製バージョンであり、パフォーマンスが向上し、サイズが小さくなります。これを使用してテキストをエンコードし、テキストの意味表現ベクトルを取得します。Transformer モデルにはトークンの長さ制限があるため、大きなドキュメントを入力するとエラーが発生する可能性があります。この場合、文書をいくつかの小さな段落に分割し、結果として得られるベクトルをプールすることを検討するかもしれません。

model = SentenceTransformer(r'xlm-r-distilroberta-base-paraphrase-v1')

4. キーワードを抽出する

入力テキストに対して、TF-IDFなどの手法を用いて一連のキーワード候補を抽出します。同時に、ngram 範囲を設定して、返されるキーワードの長さを制御します。

ここで使用されるアイデアは、テキストコンテンツの埋め込みを取得し、それをテキストタイトルの埋め込みと比較することです. 記事のキーワードはタイトルコンテンツと強い類似性を持っていることがよくあります. 候補と文書の間の類似性を計算するためにでは、高次元では非常に優れたパフォーマンスを発揮するため、ベクトル間のコサイン類似度を使用します。

ただし、この方法では、本文タイトルにすべてのキーワードが含まれているとは限らないなど、文章によっては問題が発生する場合があります。

「ニモ」の上司の考えを共有することは非常に刺激的です。

タスク 2 を検討する私の旅を簡単に共有しましょう。後で、学習ノートを改善する時間のあるブログに、より詳細な方法が記載される予定です。

最初はタスク 2 の抽出方法を使用しました。ベースラインコードは、まず単語頻度統計を使用して候補キーワードを取得し、次に候補キーワードとタイトルをベクトル化し、コサイン類似度を比較することによってキーワードを抽出します。以前データ分析をしたことがあり、キーワードが複数の単語で構成されている場合があるため、最初は1~5Gramを使ってキーワード候補を抽出していましたが、Aリストテストの結果が良くなかったので、1Gramと2Gramに切り替えました。 ,..., 5Gram はそれぞれ単語数の異なるキーワードを独立した候補群として抽出し、各候補群はコサイン類似度によりキーワードを抽出するが、このときの A リストの精度は約 20% に達する。この時、タスク2の現在の評価基準では、十分なキーワードがあれば常に全てのキーワードを正しくマスクできるバグを発見したので、主催者に報告しました。評価基準を改善するのかと思いきや、キーワード抽出数を直接制限することになってしまった。

さらにデータセットを観察したところ、タイトルや抄録にキーワードが含まれていないサンプルが多く、現時点ではキーワード抽出だけで高スコアを得るのは困難であることがわかりました。そこで、生成キーワードの抽出に目を向けましたが、生成キーワードの抽出方法は、昨日のライブ配信で共有した方法と同じですので、動画を見ていただくとして、ここでは詳しく説明しません。

その後、抽出手法と生成手法は相互補完できるのではないかと考え、TextRank アルゴリズムの研究を続けました。最終的に、コサイン類似度キーワード抽出(抽出型)+TextRankキーワード抽出(抽出型)+BART生成キーワード抽出の融合ソリューションを形成した。

おすすめ

転載: blog.csdn.net/qq_42859625/article/details/131955775
おすすめ