当前位置: 首页 > article >正文

思维链技术(Chain-of-Thought, CoT)

思维链(Chain-of-Thought, CoT)是一种通过模拟人类逐步推理过程来提升大型语言模型(LLM)复杂任务表现的技术。其核心思想是让模型在生成最终答案前,先输出中间推理步骤,从而增强逻辑性和可解释性。

1. 基础示例:数学问题求解

  • 问题: “罗杰有5个网球,他又买了两盒网球,每盒有3个网球。他现在有多少网球?”
  • 传统Prompting(直接输出答案):模型可能直接输出错误答案(如"27"),因为缺乏中间计算步骤

CoT Prompting(分步推理):

问:罗杰有5个网球,他又买了两盒网球,每盒有3个网球。他现在有多少网球?  
答:罗杰一开始有5个网球,2盒3个网球,一共是2*3=6个网球,5+6=11。答案是11。

2.复杂推理:符号操作任务
问题
“将单词 ‘hello’ 的最后一个字母与 ‘world’ 的最后一个字母拼接起来。”

CoT Prompting:

1. 'hello'的最后一个字母是'o'2. 'world'的最后一个字母是'd'3. 拼接'o''d'得到'od'。  
最终答案:od

3. 零样本思维链(Zero-shot CoT)
无需示例,仅通过指令触发推理:
问题
“若A=B+2,B=3*C,C=5,求A的值。”

指令
“让我们一步一步思考:”
模型自动生成:

1. 计算C=52. 计算B=3*5=153. 计算A=15+2=17。  
答案:17

代码示例:
准备数据

import json
import os
from typing import List, Dict, Any

# 创建CoT训练数据集
def create_cot_dataset():
    """创建包含思维链(CoT)示例的数据集"""
    
    cot_examples = [
        {
            "instruction": "解决以下数学问题,请一步一步思考。",
            "input": "罗杰有5个网球,他又买了两盒网球,每盒有3个网球。他现在有多少网球?",
            "output": "让我一步一步思考:\n1. 罗杰一开始有5个网球\n2. 他买了两盒网球,每盒有3个网球\n3. 两盒网球总共有2×3=6个网球\n4. 罗杰现在总共有5+6=11个网球\n因此,罗杰现在有11个网球。"
        },
        {
            "instruction": "解决以下数学问题,请一步一步思考。",
            "input": "一个商店以每个15元的价格卖了8个苹果,又以每个10元的价格卖了12个橘子。商店总共赚了多少钱?",
            "output": "让我一步一步思考:\n1. 苹果的收入:8个 × 15元 = 120元\n2. 橘子的收入:12个 × 10元 = 120元\n3. 总收入 = 苹果收入 + 橘子收入 = 120元 + 120元 = 240元\n因此,商店总共赚了240元。"
        },
        {
            "instruction": "解决以下逻辑问题,请一步一步思考。",
            "input": "如果所有的A都是B,所有的B都是C,那么所有的A都是C吗?",
            "output": "让我一步一步思考:\n1. 已知条件:所有的A都是B,所有的B都是C\n2. 对于任何一个A,它一定是B(根据条件1)\n3. 由于它是B,而所有的B都是C(根据条件2),所以这个A也一定是C\n4. 因此,所有的A都是C\n所以,结论\"所有的A都是C\"是正确的。",
        },
        {
            "instruction": "解决以下符号操作问题,请一步一步思考。",
            "input": "将单词'hello'的最后一个字母与'world'的最后一个字母拼接起来。",
            "output": "让我一步一步思考:\n1. 'hello'的最后一个字母是'o'\n2. 'world'的最后一个字母是'd'\n3. 将这两个字母拼接起来,得到'od'\n因此,答案是'od'。"
        },
        {
            "instruction": "解决以下代数问题,请一步一步思考。",
            "input": "若A=B+2,B=3*C,C=5,求A的值。",
            "output": "让我一步一步思考:\n1. 已知C=5\n2. 计算B:B=3*C=3*5=15\n3. 计算A:A=B+2=15+2=17\n因此,A的值是17。"
        }
    ]
    
    # 添加更多复杂的CoT示例
    more_examples = [
        {
            "instruction": "解决以下概率问题,请一步一步思考。",
            "input": "一个袋子里有3个红球和5个蓝球。如果随机抽取两个球,求抽到的两个球都是红球的概率。",
            "output": "让我一步一步思考:\n1. 袋子里总共有3+5=8个球\n2. 第一次抽取红球的概率是3/8\n3. 在第一次抽到红球的情况下,第二次抽取红球的概率是2/7(因为还剩2个红球和5个蓝球)\n4. 两次都抽到红球的概率是(3/8)×(2/7)=6/56=3/28\n因此,抽到的两个球都是红球的概率是3/28。"
        },
        {
            "instruction": "解决以下物理问题,请一步一步思考。",
            "input": "一辆汽车以60千米/小时的速度行驶了2小时,然后以80千米/小时的速度行驶了1小时。求汽车的平均速度。",
            "output": "让我一步一步思考:\n1. 第一阶段:速度60千米/小时,时间2小时,距离=60×2=120千米\n2. 第二阶段:速度80千米/小时,时间1小时,距离=80×1=80千米\n3. 总距离=120+80=200千米\n4. 总时间=2+1=3小时\n5. 平均速度=总距离/总时间=200/3≈66.67千米/小时\n因此,汽车的平均速度约为66.67千米/小时。"
        }
    ]
    
    cot_examples.extend(more_examples)
    
    # 保存数据集
    os.makedirs("d:\\Trae\\develop\\try\\data", exist_ok=True)
    with open("d:\\Trae\\develop\\try\\data\\cot_dataset.json", "w", encoding="utf-8") as f:
        json.dump(cot_examples, f, ensure_ascii=False, indent=2)
    
    print(f"已创建CoT数据集,包含{len(cot_examples)}个示例")
    return cot_examples

if __name__ == "__main__":
    create_cot_dataset()

lora微调

import os
import json
import torch
from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    Trainer,
    TrainingArguments
)
from datasets import Dataset
from peft import (
    LoraConfig,
    get_peft_model,
    prepare_model_for_kbit_training,
    TaskType
)

def load_cot_dataset(file_path: str):
    """加载CoT数据集"""
    with open(file_path, "r", encoding="utf-8") as f:
        data = json.load(f)
    return data

def prepare_dataset_for_lora(examples, tokenizer, max_length: int = 512):
    """准备用于LoRA训练的数据集"""
    formatted_examples = []
    
    for example in examples:
        # 格式化为指令微调格式
        formatted_text = f"指令: {example['instruction']}\n问题: {example['input']}\n回答: {example['output']}"
        formatted_examples.append({"text": formatted_text})
    
    # 创建数据集
    dataset = Dataset.from_list(formatted_examples)
    
    # 对数据集进行分词处理
    def tokenize_function(examples):
        return tokenizer(
            examples["text"],
            padding="max_length",
            truncation=True,
            max_length=max_length,
            return_tensors="pt"
        )
    
    tokenized_dataset = dataset.map(
        tokenize_function,
        batched=True,
        remove_columns=["text"]
    )
    
    # 划分训练集和验证集
    tokenized_dataset = tokenized_dataset.train_test_split(test_size=0.1)
    
    return tokenized_dataset

def train_cot_with_lora():
    """使用LoRA方法微调模型以学习CoT推理"""
    # 加载预训练模型和分词器
    model_name = "THUDM/chatglm3-6b"  # 可以替换为其他中文模型
    
    print(f"加载模型: {model_name}")
    tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
    model = AutoModelForCausalLM.from_pretrained(
        model_name,
        trust_remote_code=True,
        torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
        load_in_8bit=True if torch.cuda.is_available() else False
    )
    
    # 准备模型进行LoRA微调
    if torch.cuda.is_available():
        model = prepare_model_for_kbit_training(model)
    
    # 配置LoRA
    lora_config = LoraConfig(
        task_type=TaskType.CAUSAL_LM,
        r=8,  # LoRA的秩
        lora_alpha=32,
        lora_dropout=0.1,
        target_modules=["query_key_value"],  # 根据模型架构调整
        bias="none",
    )
    
    # 应用LoRA配置
    model = get_peft_model(model, lora_config)
    print(f"可训练参数数量: {model.print_trainable_parameters()}")
    
    # 加载CoT数据集
    cot_examples = load_cot_dataset("d:\\Trae\\develop\\try\\data\\cot_dataset.json")
    print(f"加载了{len(cot_examples)}个CoT示例")
    
    # 准备训练数据集
    tokenized_dataset = prepare_dataset_for_lora(cot_examples, tokenizer)
    
    # 设置训练参数
    training_args = TrainingArguments(
        output_dir="d:\\Trae\\develop\\try\\cot_lora_model",
        overwrite_output_dir=True,
        num_train_epochs=3,
        per_device_train_batch_size=4,  # 使用LoRA可以用更大的批量
        per_device_eval_batch_size=4,
        gradient_accumulation_steps=4,
        evaluation_strategy="steps",
        eval_steps=50,
        save_strategy="steps",
        save_steps=50,
        save_total_limit=3,
        learning_rate=1e-4,
        weight_decay=0.01,
        warmup_steps=50,
        logging_dir="d:\\Trae\\develop\\try\\logs",
        logging_steps=10,
        fp16=torch.cuda.is_available(),
        report_to="none",
    )
    
    # 创建训练器
    trainer = Trainer(
        model=model,
        args=training_args,
        train_dataset=tokenized_dataset["train"],
        eval_dataset=tokenized_dataset["test"],
    )
    
    # 开始训练
    print("开始LoRA训练...")
    trainer.train()
    
    # 保存模型
    model.save_pretrained("d:\\Trae\\develop\\try\\cot_lora_model\\final")
    tokenizer.save_pretrained("d:\\Trae\\develop\\try\\cot_lora_model\\final")
    print("LoRA训练完成,模型已保存")

if __name__ == "__main__":
    train_cot_with_lora()

推理

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

def load_cot_model(model_path: str):
    """加载训练好的CoT模型"""
    tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
    model = AutoModelForCausalLM.from_pretrained(
        model_path,
        trust_remote_code=True,
        torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32
    )
    return model, tokenizer

def generate_cot_response(model, tokenizer, instruction: str, problem: str, max_length: int = 1024):
    """使用CoT模型生成包含推理步骤的回答"""
    # 构建输入提示
    prompt = f"指令: {instruction}\n问题: {problem}\n回答:"
    
    # 生成回答
    inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
    outputs = model.generate(
        inputs.input_ids,
        max_length=max_length,
        temperature=0.7,
        top_p=0.9,
        do_sample=True,
        num_return_sequences=1
    )
    
    # 解码回答
    response = tokenizer.decode(outputs[0], skip_special_tokens=True)
    
    # 提取回答部分
    response = response.split("回答:")[1].strip() if "回答:" in response else response
    
    return response

def test_cot_model():
    """测试CoT模型的推理能力"""
    # 加载模型
    model_path = "d:\\Trae\\develop\\try\\cot_model\\final"
    model, tokenizer = load_cot_model(model_path)
    model.to("cuda" if torch.cuda.is_available() else "cpu")
    model.eval()
    
    # 测试问题
    test_problems = [
        {
            "instruction": "解决以下数学问题,请一步一步思考。",
            "problem": "小明有12个苹果,他给了小红3个,又给了小李2个,然后自己吃了1个。小明还剩下多少个苹果?"
        },
        {
            "instruction": "解决以下逻辑问题,请一步一步思考。",
            "problem": "如果今天不是周一,那么明天不是周二。今天是周三,那么明天是周几?"
        },
        {
            "instruction": "解决以下代数问题,请一步一步思考。",
            "problem": "若x+y=10且xy=21,求x²+y²的值。"
        }
    ]
    
    # 对每个问题生成回答
    for i, test in enumerate(test_problems):
        print(f"\n测试 {i+1}:")
        print(f"指令: {test['instruction']}")
        print(f"问题: {test['problem']}")
        
        response = generate_cot_response(model, tokenizer, test['instruction'], test['problem'])
        print(f"回答:\n{response}")
        print("-" * 50)

if __name__ == "__main__":
    test_cot_model()


http://www.kler.cn/a/614712.html

相关文章:

  • Python库()
  • docker构建并启动前端
  • Spring-MVC笔记(下)
  • 生物化学笔记:医学免疫学原理09 白细胞分化抗原+黏附分子
  • python 上下文管理器with
  • 双指针算法(快慢指针/对撞指针法)
  • coding ability 展开第七幕(前缀和算法——进阶巩固)超详细!!!!
  • RPCGC阅读
  • 媒体直播的力量:解锁新时代传播密码,引爆传播效应,媒介盒子分享
  • SpringCould微服务架构之Docker(5)
  • 大数据Spark(五十五):Spark框架及特点
  • Unity程序嵌入Qt后点击UI按钮Button没有反应
  • Nmap全脚本使用指南!NSE脚本全详细教程!Kali Linux教程!(二)
  • Redis场景问题2:缓存击穿
  • 20组电影美学RED摄像摄影机视频胶片模拟色彩分级调色LUT预设包 Pixflow – CL – RED Camera LUTs
  • Lambda 表达式调试实践指南
  • 笔记:遇见未来——6G协同创新技术研讨会
  • 家乡旅游景点小程序(源码+部署教程)
  • 【数电】半导体存储电路
  • TCP 协议深度解析