圆圆网络 手游攻略 新游动态 max微调器,微调平台

max微调器,微调平台

时间:2024-05-04 10:44:12 来源:头条 浏览:0

本指南面向那些想要为自己的项目定制强大的语言模型(例如Llama 2 或Mistral)的人。即使您无法使用超级计算机,我们也将引导您完成使用QLoRA 微调这些大规模语言模型(LLM) 的步骤。

要点:伟大的模型需要大量的数据。了解如何训练现有数据并创建您自己的数据集。了解如何格式化训练数据,特别是ChatML 格式。代码保持简单,避免了额外的黑匣子和培训工具,并且仅使用基本的PyTorch 和Hugging Face 包。您将在这里学到什么:

max微调器,微调平台

如何查找和准备数据集将数据集转换为ChatML 格式以进行训练加载基础模型的量化版本并插入LoRA 适配器选择正确的训练设置让我们开始吧!先决条件开始之前:您需要Hugging Face 的最新工具。通过在终端中运行以下命令来安装或更新这些软件包:

pip install -U加速bitsandbytes数据集peft转换器作为参考,这些是用于组合本教程的特定版本。

加速度0.24.1 位和字节0.41.1 数据集2.14.6peft 0.6.0transformers4.35.0torcp.1.0

01

创建现有数据集或您自己的数据集本节重点介绍加载或创建数据集,然后根据ChatML 结构对其进行格式化的主要过程。接下来,下一节将深入研究标记化和批处理领域。请记住,数据集的质量非常重要,将极大地影响模型的性能。数据集适合该任务非常重要。总体政策数据集可以是多种来源的混合体。例如,考虑Mistral 的OpenHermes 2 调整,它是使用来自多个数据集的大约900,000 个样本进行训练的。这些数据集通常以独立对的形式出现(其中单个样本对应于单个问题和答案)或一系列交互的形式(以Q/A、Q/A、Q/的形式)。包含问题和答案对由串联而成。 A)。本节旨在指导您如何将这些数据集转换为与您的训练场景兼容的统一格式。为了准备培训,您需要选择一种形式。我们在这里选择OpenAI的ChatML,因为它在最近的模型版本中被频繁采用,并且有潜力成为新标准。以下是ChatML 格式的示例对话(来自Open Orca 数据集):

|im_start|system你是一名AI助手。用户给你一个任务。你的目标是尽可能忠实地完成任务。在执行任务时,思考每一步并证明每一步的合理性。 |im_end||im_start |userPremise: 一名男子在木凳前滑直排轮。假设: 在长凳前享受滑冰的乐趣。选择正确答案: 考虑到前提,你能得出假设吗?从以下选项中选择: a). 是b). c). 否|im_end||im_start|assistantb). 你不能告诉Justification: 该男子正在滑直排木凳前,但你无法断定他是否玩得开心。因为他的感受没有被明确提及。 |im_end|上面的示例将被标记、批处理并输入到训练算法中。但在我们继续之前,让我们首先检查一些众所周知的数据集以及如何准备和格式化它们。如何加载Open Assistant 数据让我们从Open Assistant 数据集开始。

from datasets importload_datasetdataset=load_dataset('OpenAssistant/oasst_top1_2023-08-25') 加载后,数据集被预先分为训练部分(13,000 个条目)和测试部分(700 个条目)。

datasetDatasetDict({ train: Dataset({ features: ['text'], num_rows: 12947 }) test: Dataset({ features: ['text'], num_rows: 690 })}) 让我们看看第一个条目。

print(dataset['train'][0]['text'])|im_start|userConsigliami 5 nomi per il mio cucciolo di doberman|im_end||im_start|assistantEcco 5 nomi per il tuo cucciolo di dobermann:- Zeus- Apollo- Thor - 雅典娜- 奥丁|im_end|多方便啊!这已经是ChatML,因此您无需执行任何操作。提醒分词器和模型,字符串|im_start| 和|im_end| 是标记,不应拆分,并且|im_end| 是一个特殊标记(eos 中的“序列结束”),用于标记模型的结束除了告诉你一件事之外,如果你不这样做,模型将永远不会生成,也永远不会完成。第3 节详细介绍了如何将这些代币与llama2 和Mistra 等基本模型集成。如何加载Open Orca 数据迁移到Open Orca 后,数据集包含420 万个条目,加载后需要进行训练和测试拆分。这可以使用train_test_split 来完成。

from datasets import load_datasetdataset=load_dataset('Open-Orca/OpenOrca')dataset=dataset['train'].train_test_split(test_size=0.1) 让我们检查一下数据集的结构。这是第一个条目:

{ 'id': 'flan.2020759', 'system_prompt': '您是人工智能助手。你将被分配一个任务。您需要生成详细且长的答案。 ', 'question': '你能把这个翻译成英文吗?尽管是其中之一,但它在46 个国家中排名第24。 '}它是一个问题/答案对,是一条系统消息,描述了应该回答问题的上下文。与Open Assistant 数据集相反,您必须自行将Open Orca 数据格式化为ChatML。

def format_conversation(row): template='''|im_start|system{sys}|im_end||im_start|user{q}|im_end||im_start|assistant{a}|im_end|''''conversation=template.format ( sys=row['system_prompt'], q=row['question'], a=row['response'], ) return {'text': 对话}import os dataset=dataset.map( format_conversation, Remove_columns=dataset [ 'train'].column_names # 删除所有列;仅保留'text' num_proc=os.cpu_count() # 多线程) 数据集现在已准备好标记并馈送到训练管道。我在那里。从播客成绩单创建数据集之前,我们在Lex Friedman 的播客成绩单上训练了llama1。该任务涉及将一个以深入讨论而闻名的播客转变为模仿雷克斯说话风格的人工智能训练集。有关如何创建数据集的更多信息,请参阅Lex Fridman 播客“从转录到AI 聊天:实验”。

from datasets import load_datasetdataset=load_dataset('g-ronimo/lfpodcast')dataset=dataset['train'].train_test_split(test_size=0.1) 如果查看训练集中的第一个条目,您将看到以下JSON 对象。

print(json.dumps(dataset['train'][0],indent=2)){ 'title': 'Lex_Fridman_Podcast_-_114__Russ_Tedrake_Underactuated_Robotics_Control_Dynamics_and_Touch', 'episode': 114, 'speaker_ratio_lex-vs-guest'3336 0 0.4 44 02311303719755 , 'Conversation': [ { 'from': 'Guest', 'text': '我认为机器人最美的动作是被动动态步行者。我认为它有一些本质上美丽的东西。 (.) 但史蒂夫·安迪得出了这个美丽的结论。我创造了一个有膝盖、手臂和躯干的东西,当手臂自然摆动并稍微推动时,看起来就像在公园散步一样。 ' }, { ' from': 'Lex', 'text': '你如何设计这样的东西?它是艺术还是科学?' },(.) 这种结构虽然抓住了每个播客剧集的精髓,对话必须转换为ChatML 格式,为训练模型做准备。您需要循环遍历每一轮消息,应用ChatML 格式,然后连接消息以将整个剧集记录存储在一个文本字段中。 Guest 和Rex 的角色分别重新分配给User 和Assistant,以调整语言模型以采用Curious 和Knowledgeable Rex 的角色。

def format_conversation(row): # 对话模板将采用ChatML 格式template='''|im_start|user {q}|im_end| |im_start|assistant {a}|im_end|''' Turns=row['conversation '] # 如果Lex 是第一个发言者,则跳过他的回合并开始询问嘉宾问题if Turns[0]['from']=='Lex': Turns=turns[1:] Conference=[] for i in range(0, len (turns), 2): # 假设对话始终在guest 和Lex 之间交替Question=turns[i] # Guest 的回答=turns[i+ 1] # Lex communications.append( template.format( q=question['text' ], a=answer['text'], )) return {'text': '\n'.join(conversation)}import osdataset=dataset.map( format_conversation, Remove_columns=dataset['train'].column_names, num_proc=os.cpu_count() )应用这些更改后,生成的数据集已准备好进行标记化并输入到训练管道中,并教授语言模型对话,这让人想起Lex Fridman 的播客讨论。基于书籍创建数据集为了更好地理解数据集创建的细微差别,可以考虑训练人工智能来反映名人的声音和个性。例如,您选择将美国著名厨师Anthony Bourdain 的自传转换为数据集。他写的《厨房秘笈》,生动地描绘了厨房的疯狂和厨师的心思。这个过程包括将布尔登书中的故事转化为引人入胜的对话,比如捕捉他精神的来回采访。所需步骤:

将您的书籍转换为文本段落分析和拆分:一旦您的书籍采用文本格式,就可以对其进行拆分。短段落被合并,长段落被分解,这样每个部分都可以独立存在,同时仍然对整个故事情节做出贡献。生成面试问题:对于每一篇文章,我们都会构建一个人工面试场景,其中法学硕士扮演面试官的角色,并生成问题,引出自然适合书中特定文章的答案。目的是引发富有洞察力的对话,并给人留下布尔登本人正在回答有关他的生活和经历的问题的印象。首先,假设您合法获得了本书的数字副本kc.pdf。

mv anthony-bourdain-kitchen-confidential.pdf kc.pdfpdftotext -nopgbrk kc.pdf # 句子中正确的换行符-r ':a /[a-zA-Z,\ ]$/N;s/(.)\n/\1 /;ta' kc.txt kc_reformat.txt 现在使用每个段落n 和段落n-1 来参与智能开源LLM 或GPT-3.5/4。使用Open Hermes 2 为每个段落创建面试问题。

# 使用open('kc_reformat.txt') as f: 收集目标段落file_content=f.read()chapters=file_content.split('\n\n')# 确保正确访谈的最小和最大长度定义长度flowpassage_minlen=300 # 如果段落300 个字符- 与nextpassage_maxlen=2000 合并# 如果段落2k 个字符- split# 将章节处理成适当的采访段落paragraphs=[]for chap in Chapters: Passages='' for par in chap.split('\n'): if (len(passage)system 您是一位专家采访者,采访著名厨师的自传。您根据他们自传中的引言创建问题。下面是这样的引言。创建一个问题,引言是完美的答案。问题应该简短,就像采访一样,并且是针对自传的作者。问题应该简短。记住:问题尽可能简短。不要在问题中透露答案。此外,如果可能,请询问动机。询问感受或: 问题,而不是事件或事实。已识别。这里有一些上下文可以帮助您提出有关quote:{ctx}|im_end||im_start|userQuote:{par}|im_end||im_start|assistantQuestion:' 的问题''prompts=[ ] for i,p in enumerate(passages): Prompt=prompt_template.format(par=passages[i], ctx=passages[i-1]) Prompts.append(prompt)# 智能LLM提示,解析结果,Q/Save .json in A.在这里您可以找到对我有用的完整代码。生成的.json 文件如下所示:

{ 'question': '为什么我选择分享我在餐饮行业的职业生涯和见解,即使我想冒犯或吓唬公众?', 'answer': '我选择分享我的经验和见解我在餐饮行业的职业生涯中的见解。我不会透露我在作为洗碗机、准备无人机、油炸厨师、烧烤厨师、酱汁师、副厨师和厨师的漫长职业生涯中所看到的、学到的和学到的一切。厨师,我一直在实施。 “当这件事出来时,我仍然想成为一名厨师,因为这是我唯一真正了解的生活。如果我凌晨四点需要帮忙,即使是简单的贷款,如果我需要安眠药,保释金,或者有人在雨中在不安全的地方接我让我哭,我会打电话给一位作家同事。没有。我去叫副主厨。或者是前副主厨,或者是我那个时髦的人,是我曾经共事过或者已经共事了20多年的人。 ', 'answer': '不,我想谈谈餐厅后面的黑暗凹处。这是一种具有数百年历史的军国主义等级制度和精神的亚文化。 《朗姆酒、肛交和鞭子》创造了不屈的秩序和令人伤脑筋的混乱的结合。因为我觉得这一切都很舒服,就像洗个热水澡一样。这辈子搬家很容易。我会说语言。在纽约市由厨师和厨师组成的小型乱伦社区,我了解这里的人,也知道如何在厨房里表现(在现实生活中,在更不稳定的情况下)。我想要一位专家。 人阅读本文是为了享受现实。

a straight look at a life many of us have lived and breathed for most of our days and nights to the exclusion of 'normal' social interaction. Never having had a Friday or Saturday night off, always working holidays, being busiest when the rest of the world is just getting out of work, makes for a sometimes peculiar world-view, which I hope my fellow chefs and cooks will recognize. The restaurant lifers who read this may or may not like what I'm doing. But they'll know I'm not lying."}最后,我们再次将数据集转换为 ChatML 格式: interview_fn="kc_reformat_interview.json"dataset = load_dataset('json', data_files=interview_fn, field='interview')dataset=dataset["train"].train_test_split(test_size=0.1)# chatML template, from https://huggingface.co/docs/transformers/main/chat_templatingtokenizer.chat_template = "{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}"def format_interview(conv): messages = [ {"role": "user", "content": conv["question"]}, {"role": "assistant", "content": conv["answer"]} ] chat=tokenizer.apply_chat_template(messages, tokenize=False).strip() return {"text": chat}dataset = dataset.map( format_conversation, remove_columns=dataset["train"].column_names )通过改造 Bourdain 的自传,我们的目标是生成一个人工智能,呼应他的叙事风格和对烹饪行业的看法,并体现他的人生哲学。所提供的方法非常基础,将受益于进一步的细化,例如删除低内容答案,剥离脚注、页码等非必要文本元素。这将提高模型的质量。如果您好奇,请与Mistral Bourdain交谈。尽管当前的输出是对 Bourdain 声音的基本模仿,但它可以作为概念证明;增强的数据集管理无疑会产生更有说服力的模拟。创建您自己的数据集你现在已经明白了。以下是 GPT-4 提出的一些关于创意数据集创建的其他想法: 历史人物演讲数据集。收集历史人物的演讲、信件和书面作品,创建反映他们说话和写作风格的数据集。这可以用来生成教育内容,例如对历史人物的模拟采访,或者创建叙事体验,让这些人物对现代事件进行评论。虚构世界百科全书。根据各种奇幻和科幻小说创建数据集,详细说明这些故事中的世界构建元素,例如地理、政治体系、物种和技术。这可用于训练人工智能生成新的幻想世界或为游戏开发提供丰富的上下文信息。情感对话数据集。分析电影剧本、戏剧和小说,创建带有相应情感基调的对话数据集。该数据集可用于训练人工智能系统,该系统可识别并生成具有微妙情感底蕴的对话,这有利于改善聊天机器人和虚拟助手的移情反应。技术产品评论和规格数据集。编译来自各种来源的技术产品评论、规格和用户评论的综合数据集。该数据集可以为推荐引擎或人工智能系统提供动力,旨在为消费者提供购买建议。02 加载并准备模型和分词器在开始处理刚刚准备的数据之前,我们需要加载模型和标记生成器,并确保它们正确处理 ChatML 标签<|im_start|>并被<|im_end|>识别<|im_end|>为(新的)eos 标记。 import torchfrom transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer, BitsAndBytesConfigfrom peft import prepare_model_for_kbit_training, LoraConfig, get_peft_modelmodelpath="models/Mistral-7B-v0.1"# Load 4-bit quantized modelmodel = AutoModelForCausalLM.from_pretrained( modelpath, device_map="auto", quantization_config=BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_compute_dtype=torch.bfloat16, bnb_4bit_quant_type="nf4", ), torch_dtype=torch.bfloat16,)# Load (slow) Tokenizer, fast tokenizer sometimes ignores added tokenstokenizer = AutoTokenizer.from_pretrained(modelpath, use_fast=False) # Add tokens <|im_start|> and <|im_end|>, latter is special eos token tokenizer.pad_token = ""tokenizer.add_tokens(["<|im_start|>"])tokenizer.add_special_tokens(dict(eos_token="<|im_end|>"))model.resize_token_embeddings(len(tokenizer))model.config.eos_token_id = tokenizer.eos_token_id由于我们不是训练所有参数而只是训练一个子集,因此我们必须使用 Huggingface 将 LoRA 适配器添加到模型中peft。确保使用peft>= 0.6,否则 1)get_peft_model会非常慢,2) Mistral 训练会失败。 # Add LoRA adapters to modelmodel = prepare_model_for_kbit_training(model)config = LoraConfig( r=64, lora_alpha=16, target_modules = ['q_proj', 'k_proj', 'down_proj', 'v_proj', 'gate_proj', 'o_proj', 'up_proj'], lora_dropout=0.1, bias="none", modules_to_save = ["lm_head", "embed_tokens"], # needed because we added new tokens to tokenizer/model task_type="CAUSAL_LM")model = get_peft_model(model, config)model.config.use_cache = FalseLoRA 等级r:指定要训练的参数数量。排名越高,您训练的参数越多,适配器文件也越大。通常是 8 到 128 之间的数字。最大可能值,即。hidden_size训练所有参数,对于 llama2-7b 和 Mistral (= in )将为 4096 config.json,并且无法达到添加适配器的目的。QLoRA 论文建议64使用guanaco(开放助理数据集),这对我来说效果很好。target_modules:QLoRA 作者在论文中的另一个建议/发现:我们发现最关键的 LoRA 超参数是总共使用了多少个 LoRA 适配器,并且所有线性变压器块层上的 LoRA 都需要匹配完整的微调性能modules_to_save:指定除 LoRA 层之外的模块设置为可训练并保存在最终检查点中。由于我们将 ChatML 标签作为标记添加到词汇表中,因此我们还需要训练并保存线性层lm_head和嵌入矩阵embed_tokens。这对于稍后将适配器合并回基本模型相关。03 准备训练数据正确的标记化和批处理对于确保正确处理数据至关重要。代币化text对数据集中的字段进行标记,无需添加特殊标记或填充,因为我们将手动执行此操作。 def tokenize(element): return tokenizer( element["text"], truncation=True, max_length=2048, add_special_tokens=False, )dataset_tokenized = dataset.map( tokenize, batched=True, num_proc=os.cpu_count(), # multithreaded remove_columns=["text"] # don't need the strings anymore, we have tokens from here on)max_length:指定样本的最大长度(以标记数为单位)。所有超过 2048 个令牌的内容都将被截断并且不会进行训练。如果您的数据集在单个样本中只有简短的问题/答案对(例如 Open Orca),那么这将绰绰有余,如果您的样本较长(例如播客转录本),则理想情况下您将增加(消耗 VRAM)或max_length拆分将您的样本分成几个较小的样本。llama2 的最大值是 4096。Mistral 是“使用 8k 上下文长度和固定缓存大小进行训练,理论注意力跨度为 128k 令牌”,但我从未超过 4096。配料Hugging Facetrainer需要一个整理器函数将样本列表转换为包含一批填充的字典 input_ids(标记化文本)labels(目标文本,与 相同input_ids)和attention_masks(零和一的张量)。为此,我们将采用QLoRA 存储库DataCollatorForCausalLM中的简化版本。 04 训练超参数超参数的选择可以显着影响模型性能。以下是我们为训练选择的超参数: bs=8 # batch sizega_steps=1 # gradient acc. stepsepochs=5steps_per_epoch=len(dataset_tokenized["train"])//(bs*ga_steps)args = TrainingArguments( output_dir="out", per_device_train_batch_size=bs, per_device_eval_batch_size=bs, evaluation_strategy="steps", logging_steps=1, eval_steps=steps_per_epoch, # eval and save once per epochsave_steps=steps_per_epoch, gradient_accumulation_steps=ga_steps, num_train_epochs=epochs, lr_scheduler_type="constant", optim="paged_adamw_32bit", learning_rate=0.0002, group_by_length=True, fp16=True, ddp_find_unused_parameters=False, # needed for training with accelerate)batch size:尽可能高以提高速度。消耗VRAM,如果OOM则减少。gradient_accumulation_steps:增加有效批量大小而不消耗额外的 VRAM,但会使训练速度变慢。有效批量大小为batch_size* gradient_accumulation_steps。steps_per_epoch:如果您的数据集有 80 个样本,并且有效批量大小为 8(例如batch_size8 和gradient_accumulation_steps1),您将通过 10 个步骤(= 1 epoch)处理整个数据集。num_train_epochs:训练多少个纪元取决于您的数据集。理想情况下,eval split 上的损失会告诉您何时停止训练以及哪个检查点是最好的 -但例如,训练guanaco 会导致在第 2 轮之后 eval_loss 增加,这表明对训练集的过度拟合,即使模型质量有所提高。有关此内容的更多信息以及 QLoRA 作者在github上和我之前的一篇文章中的官方回复。总而言之:您只需查看哪个检查点最适合您的特定任务。通常,3-4 个 epoch 是一个好的开始。learning_rate:我们将使用 QLoRA 作者建议的默认学习率,对于 7B(或 13 B)模型为 0.0002。对于参数较多的模型,建议较低的学习率:33B 和 65B 参数的模型为 0.0001。我们来训练吧。 trainer = Trainer( model=model, tokenizer=tokenizer, data_collator=collate, train_dataset=dataset_tokenized["train"], eval_dataset=dataset_tokenized["test"], args=args,)trainer.train()05 训练运行示例训练和评估损失下面是 Open Assistant (OA) 数据集典型训练运行的 wandb 图表,比较了 llama2–7b 和 Mistral-7b 的微调。 训练时间和 VRAM 使用情况在具有 24GB VRAM 的单个 GPU 上对 Open Assistant 数据集上的 Llama2–7B 和 Mistral-7B 进行微调每个周期大约需要 100 分钟。 显卡:NVIDIA GeForce RTX 3090数据集“OpenAssistant/oasst_top1_2023–08–25”批量大小 16, grad. acc. steps 1样品max_length512将 LoRA 适配器与基本模型合并以下代码与其他脚本(例如TheBloke 提供的脚本)有点不同,因为我们在训练之前添加了 ChatML 的标记。不过,我们没有更改基本模型,这就是为什么在加载适配器之前我们必须将新标记添加到基本模型和标记生成器中;否则,我们将尝试将具有两个附加令牌的适配器合并到没有这些令牌的模型中(这将失败)。 from transformers import AutoModelForCausalLM, AutoTokenizerfrom peft import PeftModelimport torchbase_path="models/Mistral-7B-v0.1" # input: base modeladapter_path="out/checkpoint-606" # input: adapterssave_to="models/Mistral-7B-finetuned" # out: merged model ready for inferencebase_model = AutoModelForCausalLM.from_pretrained( base_path, return_dict=True, torch_dtype=torch.bfloat16, device_map="auto",)tokenizer = AutoTokenizer.from_pretrained(base_path)# Add/set tokens (same 5 lines of code we used before training)tokenizer.pad_token = ""tokenizer.add_tokens(["<|im_start|>"])tokenizer.add_special_tokens(dict(eos_token="<|im_end|>"))base_model.resize_token_embeddings(len(tokenizer))base_model.config.eos_token_id = tokenizer.eos_token_id# Load LoRA adapter and mergemodel = PeftModel.from_pretrained(base_model, adapter_path)model = model.merge_and_unload()model.save_pretrained(save_to, safe_serialization=True, max_shard_size='4GB')tokenizer.save_pretrained(save_to)故障排除挑战是模型训练的重要组成部分。让我们讨论一些常见问题及其解决方案。OOM如果遇到内存不足 (OOM) 错误: 考虑减少批量大小。通过减少上下文长度(max_lengthin tokenize())来缩短训练样本。训练太慢如果训练看起来缓慢: 增加批量大小。多个 GPU,购买或租用(例如在runpod上)。此处提供的代码已准备好加速,并且可用于在多 GPU 设置中进行训练,只需使用而accelerate launch qlora.py不是启动即可python qlora.py。最终模型的质量很差模型的质量反映了数据集的质量。提高模型质量: 确保您的数据集丰富且相关。研究增强数据集以获得更好表示的策略。06 总 结 明白你在做什么。有像axolotl这样优秀的训练工具,它可以让您专注于数据集创建,而不是编写自己的填充函数。尽管如此,对基本机制的扎实掌握仍然是无价的。这些知识使您能够自信地应对复杂性并排除故障。增量方法:从使用小数据集的基本示例开始。逐步扩大规模并逐步调整参数,以揭示它们对模型性能的影响。强调数据质量:高质量的数据是有效训练的基石。在组装数据集时要保持创新和勤奋。对 Llama 2 和 Mistral 等 LLM 进行微调是一个有益的过程,特别是当您拥有正确的数据集和训练参数时。请记住始终关注模型的性能并准备好迭代和适应。
标题:max微调器,微调平台
链接:https://yyuanw.com/news/xydt/13905.html
版权:文章转载自网络,如有侵权,请联系删除!
资讯推荐
更多
阴阳师4月22日更新内容:帝释天上线技能调整,红莲华冕活动来袭

阴阳师4月22日更新内容:帝释天上线技能调整,红莲华冕活动来袭[多图],阴阳师4月22日更新的内容有哪些?版本更新

2024-05-04
四川电视台经济频道如何培养孩子的学习习惯与方法直播在哪看?直播视频回放地址

四川电视台经济频道如何培养孩子的学习习惯与方法直播在哪看?直播视频回放地址[多图],2021四川电视台经济频

2024-05-04
湖北电视台生活频道如何培养孩子的学习兴趣直播回放在哪看?直播视频回放地址入口

湖北电视台生活频道如何培养孩子的学习兴趣直播回放在哪看?直播视频回放地址入口[多图],湖北电视台生活频道

2024-05-04
小森生活金币不够用怎么办?金币没了不够用解决方法

小森生活金币不够用怎么办?金币没了不够用解决方法[多图],小森生活金币突然就不够用的情况很多人都有,金币没

2024-05-04