Skip to content

Latest commit

 

History

History
156 lines (125 loc) · 8 KB

2.2 数据预处理.md

File metadata and controls

156 lines (125 loc) · 8 KB

1 数据预处理

在这一章节,我们将针对于BERT的两个任务MLMNSP构建可用于模型训练的数据集。

1.2

  • 构建相邻语句预测数据集
  1. 定义相似句预测任务,解释任务目标和构建数据集方法。
  2. 通过采集大量文本数据,利用句号等标点符号分割句子,构建标签为1的相邻句对和标签为0的非相邻句对。
  3. 解决数据集中可能存在的无句号或超长句子问题,通过切分句子长度保证模型训练效果。
  • Mask语言模型及其实现
  1. Mask语言模型通过在文本中随机掩盖一定比例的词,强迫模型学习上下文以预测被掩盖的词,从而提高模型对语言的理解能力。
  2. 在实现Mask语言模型时,选择掩盖的词的比例、掩盖的方式(如直接掩盖12%、替换为特定标记符1.5%或随机替换为其他词1.5%)等参数对模型的训练效果有重要影响。

代码data_process将处理data.csv文件,并基于这些文本内容构建用于模型训练的数据集。

  1. 数据读取

    • 使用read_data函数从名为data.csv的文件中读取数据,特别是其中的content列,该列包含了一系列的文本内容。这些文本内容被提取并存储在一个列表中。
    def read_data():
     all_data = pd.read_csv(os.path.join("data.csv"))
     all_text = all_data["content"].tolist()
     return all_text
  2. 文本分割

    • split_text函数负责将每个文本内容按照指定的标点符号(如中文的逗号、顿号、冒号、分号、句号、问号)进行分割,形成一系列的句子或短语。
def split_text(text):
    patten = r"[,、:;。?]"
    sp_text = re.split(patten, text)
    new_sp_text = resplit_text(sp_text)
    return new_sp_text
  • resplit_text函数进一步对这些分割后的文本进行随机合并或单独保留,以形成新的文本片段列表。这个过程包括了一定的随机性,这里的随机性参考论文内容。
  1. 在每个输入序列中,随机选择15%的单词进行遮蔽。
  2. 对于被选择的每个单词,有80%的概率将其替换为[MASK]标记。
  3. 有10%的概率将其替换为一个随机选择的单词。
  4. 有10%的概率保持单词不变。
def resplit_text(text_list):
 result = []
 sentence = ""
 for text in text_list:
     if len(text) < 3:
         continue
     if sentence == "":
         if random.random() < 0.2:
             result.append(text + "。")
             continue

     if len(sentence) < 30 or random.random() < 0.2:
         sentence += text + ","
     else:
         result.append(sentence[:-1] + "。")
         sentence = text

 return result
  1. 构建数据集
    • build_neg_pos_data函数用于生成正负样本对。对于给定的文本列表,它首先创建一个正样本对(相邻的两个文本),然后为每个正样本创建一个负样本对(其中一个文本与列表中随机选择的另一个文本配对)。正负样本的标签分别为10。第一个文本片段(句子A)使用A嵌入,第二个文本片段(句子B)使用B嵌入。50%的情况下,句子B是句子A之后的实际下文,即它们在原始文本中是连续的。另外50%的情况下,句子B是从语料库中随机选择的一个句子,与句子A没有实际的上下文关系。
    def build_neg_pos_data(text_list):
     all_text1, all_text2 = [], []
     all_label = []
    
     for tidx, text in enumerate(text_list):
         if tidx == len(text_list) - 1:
             break
         all_text1.append(text)
         all_text2.append(text_list[tidx + 1])
         all_label.append(1)
    
         c_id = [i for i in range(len(text_list)) if i != tidx and i != tidx + 1]
    
         other_idx = random.choice(c_id)
    
         other_text = text_list[other_idx]
         all_text1.append(text)
         all_text2.append(other_text)
         all_label.append(0)
    
     return all_text1, all_text2, all_label
    • build_task2_dataset函数利用上述的文本分割和正负样本生成逻辑,为整个文本列表构建一个新的数据集,该数据集包含两个文本列(text1text2)和一个标签列(label),并将这些数据保存到一个新的CSV文件(task2.csv)中。
def build_task2_dataset(text_list):
    all_text1 = []
    all_text2 = []
    all_label = []

    for text in tqdm(text_list):
        sp_text = split_text(text)
        if len(sp_text) <= 2:
            continue
        text1, text2, label = build_neg_pos_data(sp_text)

        all_text1.extend(text1)
        all_text2.extend(text2)
        all_label.extend(label)

    pd.DataFrame({"text1": all_text1, "text2": all_text2, "label": all_label}).to_csv(
        os.path.join("..", "data", "task2.csv"), index=False)
  1. 构建词汇索引
    • build_word_2_index函数负责为所有文本内容构建一个词汇到索引的映射(word_2_index)以及一个索引到词汇的映射(index_2_word)。这个映射对于将文本转换为数字表示(即词嵌入)在后续的机器学习或深度学习模型中非常有用。
def build_word_2_index(all_text):
    if os.path.exists("index_2_word.txt") == True:
        with open("index_2_word.txt", encoding="utf-8") as f:
            index_2_word = f.read().split("\n")
            word_2_index = {w: idx for idx, w in enumerate(index_2_word)}
            return word_2_index, index_2_word
    word_2_index = {"[PAD]": 0, "[unused1]": 1, "[CLS]": 2, "[SEP]": 3, "[MASK]": 4, "[UNK]": 5, }

    for text in all_text:
        for w in text:
            if w not in word_2_index:
                word_2_index[w] = len(word_2_index)
    index_2_word = list(word_2_index)

    with open("index_2_word.txt", "w", encoding="utf-8") as f:
        f.write("\n".join(index_2_word))

    return word_2_index, index_2_word
  • 如果index_2_word.txt文件已经存在,则直接从该文件中读取索引到词汇的映射,并构建词汇到索引的映射。如果不存在,则根据所有文本内容动态生成这两个映射,并将索引到词汇的映射保存到index_2_word.txt文件中。
  1. 主函数
    • 在主函数中,首先读取文本数据,然后构建task2数据集,并最后为所有文本内容构建词汇索引。
all_text = read_data()
build_task2_dataset(all_text)
word_2_index = build_word_2_index(all_text)
print("")

至此,我们已经完成数据集的构建任务,并且将数据保存为index_2_word.txttask2.csv

附: 在BERT论文中,这些特殊标记具有以下含义:

  1. "[PAD]":这是一个填充标记(Padding Token),用于将所有输入序列统一到相同的长度。在处理不等长的文本数据时,较短的序列会用[PAD]标记填充,以便能够批量处理。

  2. "[unused1]":这个标记在BERT的实现中并不使用,它是一个保留的标记,可能用于未来的扩展或者特定用途,但在当前的BERT模型中没有被赋予具体功能。

  3. "[CLS]":这是一个特殊的分类标记(Classification Token)。在BERT模型中,这个标记通常放在输入序列的开始位置。经过模型处理后,[CLS]标记的最终隐藏状态被用作整个输入序列的聚合表示,尤其在分类任务中。

  4. "[SEP]":这是一个分隔标记(Separator Token),用于分隔输入序列中的不同部分,比如句子对中的句子。在句子对任务中,[SEP]标记用来区分两个句子的边界。

  5. "[MASK]":这是一个掩码标记(Mask Token),用于BERT的预训练任务之一——Masked Language Model(MLM)。在MLM任务中,输入序列中的一些单词会被随机替换为[MASK]标记,模型的任务是预测这些被掩码的单词。

  6. "[UNK]":这是一个未知词标记(Unknown Token),用于表示词汇表中不存在的单词。当输入文本中的单词不在模型的词汇表内时,会用[UNK]标记来代替。