IT评测·应用市场-qidao123.com技术社区

标题: 手把手带你了解和实践扩充 LLaMA 大语言模型的 tokenizer 模型(实现中文to [打印本页]

作者: 丝    时间: 2024-9-27 20:14
标题: 手把手带你了解和实践扩充 LLaMA 大语言模型的 tokenizer 模型(实现中文to
现在,大语言模型呈发作式的增长,此中,基于llama家族的模型占据了半壁江山。而原始的llama模型对中文的支持不太友好,接下来本文将讲解如何去扩充vocab里面的词以对中文举行token化。
一般的,现在比较主流的是利用sentencepiece练习中文词库。安装指令也很简单:pip install sentencepiece。然后,我们准备好语料,这里我们利用的语料是斗破苍穹小说。
  1. with open("data.txt", "r", encoding="utf-8") as fp:
  2.     data = fp.read().strip().split("\n")
  3. sentences = []
  4. for d in data:
  5.     d = d.strip()
  6.     if "===" in d or len(d) == 0 or d == "《斗破苍穹》来自:":
  7.         continue
  8.     sentences.append(d)
  9. with open("corpus.txt", "w", encoding="utf-8") as fp:
  10.     fp.write("\n".join(sentences))
复制代码
  1. !pip install sentencepiece
复制代码
  1. Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
  2. Requirement already satisfied: sentencepiece in e:\miniconda3\lib\site-packages (0.1.99)
  3. DEPRECATION: Loading egg at e:\miniconda3\lib\site-packages\whisper_live-0.0.11-py3.11.egg is deprecated. pip 24.3 will enforce this behaviour change. A possible replacement is to use pip for package installation.. Discussion can be found at https://github.com/pypa/pip/issues/12330
复制代码
开始练习,这里面有几个参数要留意一下,model_type分词算法选择bpe,split_digits为True,byte_fallback为True,和LLaMa 保持同等,max_sentence_length设置的大一点,更多参数解释可以检察:https://zhuanlan.zhihu.com/p/655281268 和 https://zhuanlan.zhihu.com/p/639144223
  1. import sentencepiece as spm
  2. spm.SentencePieceTrainer.train(
  3.     input='corpus.txt',
  4.     input_format='text',
  5.     model_prefix='tokenizer',
  6.     vocab_size=10000,
  7.     character_coverage=0.9995,
  8.     model_type="bpe",
  9.     num_threads=32,
  10.     split_digits=True,
  11.     byte_fallback=True,
  12.     max_sentence_length=24000
  13. )
复制代码
执行上述练习过程,大概须要30S左右,会在当前目次下生成三个文件,tokenizer.model,tokenizer.vocab。看一下模型的分词结果:
  1. import sentencepiece as spm
  2. sp_bpe = spm.SentencePieceProcessor()
  3. sp_bpe.load('tokenizer.model')
  4. print(sp_bpe.encode_as_pieces('The excellence of a translation can only be judged by noting'))
  5. print('分词长度:', len(sp_bpe.encode_as_pieces('The excellence of a translation can only be judged by noting')))
  6. print(sp_bpe.encode_as_pieces('麒麟,是中国古代神话中的一种瑞兽'))
  7. print('分词长度:', len(sp_bpe.encode_as_pieces('麒麟,是中国古代神话中的一种瑞兽')))
复制代码
  1. ['▁', '<0x54>', 'h', 'e', '▁', 'e', 'x', 'c', 'e', 'l', 'l', 'e', 'n', 'c', 'e', '▁', 'o', 'f', '▁', 'a', '▁', 't', 'r', 'a', 'n', 's', 'l', 'a', 't', 'i', 'o', 'n', '▁', 'c', 'a', 'n', '▁', 'o', 'n', 'l', 'y', '▁', 'b', 'e', '▁', 'j', 'u', 'd', 'g', 'e', 'd', '▁', 'b', 'y', '▁', 'n', 'o', 't', 'i', 'n', 'g']
  2. 分词长度: 61
  3. ['▁', '<0xE9>', '<0xBA>', '<0x92>', '麟', ',', '是', '中', '国', '古', '代', '神', '话', '中', '的一种', '<0xE7>', '<0x91>', '<0x9E>', '兽']
  4. 分词长度: 19
复制代码
可以看到,因为练习语料几乎都是中文的,对中文的分词结果是好于英文的,中文常见的一些词都变成了一个token,而英文被分的很碎。接下里把这个词表和原生LLaMa的词表举行归并。
  1. import os
  2. os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"]="python"
  3. from transformers import LlamaTokenizer
  4. from sentencepiece import sentencepiece_model_pb2 as sp_pb2_model
  5. import sentencepiece as spm
  6. # 位置
  7. llama_tokenizer_dir = "llama2-7b-hf" # llama2模型
  8. chinese_sp_model_file ="tokenizer.model" # 刚才训练的模型
  9. # 加载
  10. llama_tokenizer = LlamaTokenizer.from_pretrained(llama_tokenizer_dir)
  11. chinese_sp_model = spm.SentencePieceProcessor()
  12. chinese_sp_model.Load(chinese_sp_model_file)
  13. llama_spm = sp_pb2_model.ModelProto()
  14. llama_spm.ParseFromString(llama_tokenizer.sp_model.serialized_model_proto())
  15. chinese_spm = sp_pb2_model.ModelProto()
  16. chinese_spm.ParseFromString(chinese_sp_model.serialized_model_proto())
  17. # 打印两个词表的大小和原llama的特殊token
  18. print(f'llama2的词表大小为{len(llama_tokenizer)}, 刚训练的模型的词表大小为{len(chinese_sp_model)}')
  19. print(llama_tokenizer.all_special_tokens) # 特殊token
  20. print(llama_tokenizer.all_special_ids) # 特殊token对应的id
  21. print(llama_tokenizer.special_tokens_map)
复制代码
  1. llama2的词表大小为32000, 刚训练的模型的词表大小为10000
  2. ['<s>', '</s>', '<unk>']
  3. [1, 2, 0]
  4. {'bos_token': '<s>', 'eos_token': '</s>', 'unk_token': '<unk>'}
复制代码
开始往llama词表里添加,这里你也可以直接参加你想要参加词表的词,或者是领域内的特殊词
  1. llama_spm_tokens_set=set(p.piece for p in llama_spm.pieces)
  2. print(f"添加词表前,词表大小为:{len(llama_spm_tokens_set)}")
  3. for p in chinese_spm.pieces:
  4.     piece = p.piece
  5.     if piece not in llama_spm_tokens_set:
  6.         new_p = sp_pb2_model.ModelProto().SentencePiece()
  7.         new_p.piece = piece
  8.         new_p.score = 0
  9.         llama_spm.pieces.append(new_p)
  10. print(f"新合并词表的大小为: {len(llama_spm.pieces)}")
复制代码
  1. 添加词表前,词表大小为:32000
  2. 新合并词表的大小为: 41013
复制代码
41013-32000=9013,可以大小9013跟我们练习的10000词不相等,这是因为归并过程会默认举行去重操纵,去重后的新归并的词表大小为9013。
  1. # 保存合并后的模型
  2. output_sp_dir = 'merged_tokenizer_sp_test'
  3. output_hf_dir = 'merged_tokenizer_hf_test'
  4. os.makedirs(output_sp_dir,exist_ok=True)
  5. os.makedirs(output_hf_dir,exist_ok=True)
  6. with open(output_sp_dir+'/chinese_llama.model', 'wb') as f:
  7.     f.write(llama_spm.SerializeToString())
  8. tokenizer = LlamaTokenizer(vocab_file=output_sp_dir+'/chinese_llama.model')
  9. tokenizer.save_pretrained(output_hf_dir)
  10. print(f"Chinese-LLaMA tokenizer has been saved to {output_hf_dir}")
复制代码
  1. You are using the default legacy behaviour of the <class 'transformers.models.llama.tokenization_llama.LlamaTokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
  2. Chinese-LLaMA tokenizer has been saved to merged_tokenizer_hf_test
复制代码
  1. # 看一下效果
  2. llama_tokenizer = LlamaTokenizer.from_pretrained(llama_tokenizer_dir)
  3. chinese_llama_tokenizer = LlamaTokenizer.from_pretrained(output_hf_dir)
  4. text = "The excellence of a translation can only be judged by noting"
  5. print("原始文本:",text)
  6. print(f"llama进行token词表分割:{llama_tokenizer.tokenize(text)}")
  7. print(f"llama进行token词表长度为:{len(llama_tokenizer.tokenize(text))}")
  8. print(f"新合并的token模型词表分割:{chinese_llama_tokenizer.tokenize(text)}")
  9. print(f"新合并的token模型词表长度为:{len(chinese_llama_tokenizer.tokenize(text))}")
复制代码
  1. 原始文本: The excellence of a translation can only be judged by noting
  2. llama进行token词表分割:['▁The', '▁excell', 'ence', '▁of', '▁a', '▁translation', '▁can', '▁only', '▁be', '▁jud', 'ged', '▁by', '▁not', 'ing']
  3. llama进行token词表长度为:14
  4. 新合并的token模型词表分割:['The', '▁excell', 'ence', '▁of', '▁a', '▁translation', '▁can', '▁only', '▁be', '▁jud', 'ged', '▁by', '▁not', 'ing']
  5. 新合并的token模型词表长度为:14
复制代码
可以看到在英文上是没有变化的
  1. text = "麒麟,是中国古代神话中的一种瑞兽"
  2. print("Test text:\n",text)
  3. print("原始文本:",text)
  4. print(f"llama进行token词表分割:{llama_tokenizer.tokenize(text)}")
  5. print(f"llama进行token词表长度为:{len(llama_tokenizer.tokenize(text))}")
  6. print(f"新合并的token模型词表分割:{chinese_llama_tokenizer.tokenize(text)}")
  7. print(f"新合并的token模型词表长度为:{len(chinese_llama_tokenizer.tokenize(text))}")
复制代码
  1. Test text:
  2. 麒麟,是中国古代神话中的一种瑞兽
  3. 原始文本: 麒麟,是中国古代神话中的一种瑞兽
  4. llama进行token词表分割:['▁', '<0xE9>', '<0xBA>', '<0x92>', '<0xE9>', '<0xBA>', '<0x9F>', ',', '是', '中', '国', '古', '代', '神', '话', '中', '的', '一', '种', '<0xE7>', '<0x91>', '<0x9E>', '<0xE5>', '<0x85>', '<0xBD>']
  5. llama进行token词表长度为:25
  6. 新合并的token模型词表分割:['<0xE9>', '<0xBA>', '<0x92>', '麟', ',', '是', '中', '国', '古', '代', '神', '话', '中的', '一种', '<0xE7>', '<0x91>', '<0x9E>', '兽']
  7. 新合并的token模型词表长度为:18
复制代码
至此,我们完成了LLaMa中文词表的扩充,扩充垂直领域词表也是云云,要准备垂直领域的练习语料,最好和通用领域的练习语料混合一下

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/) Powered by Discuz! X3.4