马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
现在,大语言模型呈发作式的增长,此中,基于llama家族的模型占据了半壁江山。而原始的llama模型对中文的支持不太友好,接下来本文将讲解如何去扩充vocab里面的词以对中文举行token化。
一般的,现在比较主流的是利用sentencepiece练习中文词库。安装指令也很简单:pip install sentencepiece。然后,我们准备好语料,这里我们利用的语料是斗破苍穹小说。
- with open("data.txt", "r", encoding="utf-8") as fp:
- data = fp.read().strip().split("\n")
- sentences = []
- for d in data:
- d = d.strip()
- if "===" in d or len(d) == 0 or d == "《斗破苍穹》来自:":
- continue
- sentences.append(d)
- with open("corpus.txt", "w", encoding="utf-8") as fp:
- fp.write("\n".join(sentences))
复制代码- !pip install sentencepiece
复制代码- Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
- Requirement already satisfied: sentencepiece in e:\miniconda3\lib\site-packages (0.1.99)
- 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
- import sentencepiece as spm
- spm.SentencePieceTrainer.train(
- input='corpus.txt',
- input_format='text',
- model_prefix='tokenizer',
- vocab_size=10000,
- character_coverage=0.9995,
- model_type="bpe",
- num_threads=32,
- split_digits=True,
- byte_fallback=True,
- max_sentence_length=24000
- )
复制代码 执行上述练习过程,大概须要30S左右,会在当前目次下生成三个文件,tokenizer.model,tokenizer.vocab。看一下模型的分词结果:
- import sentencepiece as spm
- sp_bpe = spm.SentencePieceProcessor()
- sp_bpe.load('tokenizer.model')
- print(sp_bpe.encode_as_pieces('The excellence of a translation can only be judged by noting'))
- print('分词长度:', len(sp_bpe.encode_as_pieces('The excellence of a translation can only be judged by noting')))
- print(sp_bpe.encode_as_pieces('麒麟,是中国古代神话中的一种瑞兽'))
- print('分词长度:', len(sp_bpe.encode_as_pieces('麒麟,是中国古代神话中的一种瑞兽')))
复制代码- ['▁', '<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']
- 分词长度: 61
- ['▁', '<0xE9>', '<0xBA>', '<0x92>', '麟', ',', '是', '中', '国', '古', '代', '神', '话', '中', '的一种', '<0xE7>', '<0x91>', '<0x9E>', '兽']
- 分词长度: 19
复制代码 可以看到,因为练习语料几乎都是中文的,对中文的分词结果是好于英文的,中文常见的一些词都变成了一个token,而英文被分的很碎。接下里把这个词表和原生LLaMa的词表举行归并。
- import os
- os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"]="python"
- from transformers import LlamaTokenizer
- from sentencepiece import sentencepiece_model_pb2 as sp_pb2_model
- import sentencepiece as spm
- # 位置
- llama_tokenizer_dir = "llama2-7b-hf" # llama2模型
- chinese_sp_model_file ="tokenizer.model" # 刚才训练的模型
- # 加载
- llama_tokenizer = LlamaTokenizer.from_pretrained(llama_tokenizer_dir)
- chinese_sp_model = spm.SentencePieceProcessor()
- chinese_sp_model.Load(chinese_sp_model_file)
- llama_spm = sp_pb2_model.ModelProto()
- llama_spm.ParseFromString(llama_tokenizer.sp_model.serialized_model_proto())
- chinese_spm = sp_pb2_model.ModelProto()
- chinese_spm.ParseFromString(chinese_sp_model.serialized_model_proto())
- # 打印两个词表的大小和原llama的特殊token
- print(f'llama2的词表大小为{len(llama_tokenizer)}, 刚训练的模型的词表大小为{len(chinese_sp_model)}')
- print(llama_tokenizer.all_special_tokens) # 特殊token
- print(llama_tokenizer.all_special_ids) # 特殊token对应的id
- print(llama_tokenizer.special_tokens_map)
复制代码- llama2的词表大小为32000, 刚训练的模型的词表大小为10000
- ['<s>', '</s>', '<unk>']
- [1, 2, 0]
- {'bos_token': '<s>', 'eos_token': '</s>', 'unk_token': '<unk>'}
复制代码 开始往llama词表里添加,这里你也可以直接参加你想要参加词表的词,或者是领域内的特殊词
- llama_spm_tokens_set=set(p.piece for p in llama_spm.pieces)
- print(f"添加词表前,词表大小为:{len(llama_spm_tokens_set)}")
- for p in chinese_spm.pieces:
- piece = p.piece
- if piece not in llama_spm_tokens_set:
- new_p = sp_pb2_model.ModelProto().SentencePiece()
- new_p.piece = piece
- new_p.score = 0
- llama_spm.pieces.append(new_p)
- print(f"新合并词表的大小为: {len(llama_spm.pieces)}")
复制代码- 添加词表前,词表大小为:32000
- 新合并词表的大小为: 41013
复制代码 41013-32000=9013,可以大小9013跟我们练习的10000词不相等,这是因为归并过程会默认举行去重操纵,去重后的新归并的词表大小为9013。
- # 保存合并后的模型
- output_sp_dir = 'merged_tokenizer_sp_test'
- output_hf_dir = 'merged_tokenizer_hf_test'
- os.makedirs(output_sp_dir,exist_ok=True)
- os.makedirs(output_hf_dir,exist_ok=True)
- with open(output_sp_dir+'/chinese_llama.model', 'wb') as f:
- f.write(llama_spm.SerializeToString())
- tokenizer = LlamaTokenizer(vocab_file=output_sp_dir+'/chinese_llama.model')
- tokenizer.save_pretrained(output_hf_dir)
- print(f"Chinese-LLaMA tokenizer has been saved to {output_hf_dir}")
复制代码- 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
- Chinese-LLaMA tokenizer has been saved to merged_tokenizer_hf_test
复制代码- # 看一下效果
- llama_tokenizer = LlamaTokenizer.from_pretrained(llama_tokenizer_dir)
- chinese_llama_tokenizer = LlamaTokenizer.from_pretrained(output_hf_dir)
- text = "The excellence of a translation can only be judged by noting"
- print("原始文本:",text)
- print(f"llama进行token词表分割:{llama_tokenizer.tokenize(text)}")
- print(f"llama进行token词表长度为:{len(llama_tokenizer.tokenize(text))}")
- print(f"新合并的token模型词表分割:{chinese_llama_tokenizer.tokenize(text)}")
- print(f"新合并的token模型词表长度为:{len(chinese_llama_tokenizer.tokenize(text))}")
复制代码- 原始文本: The excellence of a translation can only be judged by noting
- llama进行token词表分割:['▁The', '▁excell', 'ence', '▁of', '▁a', '▁translation', '▁can', '▁only', '▁be', '▁jud', 'ged', '▁by', '▁not', 'ing']
- llama进行token词表长度为:14
- 新合并的token模型词表分割:['The', '▁excell', 'ence', '▁of', '▁a', '▁translation', '▁can', '▁only', '▁be', '▁jud', 'ged', '▁by', '▁not', 'ing']
- 新合并的token模型词表长度为:14
复制代码 可以看到在英文上是没有变化的
- text = "麒麟,是中国古代神话中的一种瑞兽"
- print("Test text:\n",text)
- print("原始文本:",text)
- print(f"llama进行token词表分割:{llama_tokenizer.tokenize(text)}")
- print(f"llama进行token词表长度为:{len(llama_tokenizer.tokenize(text))}")
- print(f"新合并的token模型词表分割:{chinese_llama_tokenizer.tokenize(text)}")
- print(f"新合并的token模型词表长度为:{len(chinese_llama_tokenizer.tokenize(text))}")
复制代码- Test text:
- 麒麟,是中国古代神话中的一种瑞兽
- 原始文本: 麒麟,是中国古代神话中的一种瑞兽
- llama进行token词表分割:['▁', '<0xE9>', '<0xBA>', '<0x92>', '<0xE9>', '<0xBA>', '<0x9F>', ',', '是', '中', '国', '古', '代', '神', '话', '中', '的', '一', '种', '<0xE7>', '<0x91>', '<0x9E>', '<0xE5>', '<0x85>', '<0xBD>']
- llama进行token词表长度为:25
- 新合并的token模型词表分割:['<0xE9>', '<0xBA>', '<0x92>', '麟', ',', '是', '中', '国', '古', '代', '神', '话', '中的', '一种', '<0xE7>', '<0x91>', '<0x9E>', '兽']
- 新合并的token模型词表长度为:18
复制代码 至此,我们完成了LLaMa中文词表的扩充,扩充垂直领域词表也是云云,要准备垂直领域的练习语料,最好和通用领域的练习语料混合一下
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |