【Datawhale夏令营】 大模型技术方向笔记

#AI夏令营 #Datawhale #夏令营

实践步骤:跑通baseline → 尝试个人idea→尝试进阶baseline

一、跑通baseline

跑通用时18分钟,评分18.2727

Step2:配置导入


from sparkai.llm.llm import ChatSparkLLM, ChunkPrintHandler
from sparkai.core.messages import ChatMessage
import json


#星火认知大模型Spark3.5 Max的URL值,其他版本大模型URL值请前往文档(https://www.xfyun.cn/doc/spark/Web.html)查看
SPARKAI_URL = 'wss://spark-api.xf-yun.com/v3.5/chat'
#星火认知大模型调用秘钥信息,请前往讯飞开放平台控制台(https://console.xfyun.cn/services/bm35)查看
SPARKAI_APP_ID = ''
SPARKAI_API_SECRET = ''
SPARKAI_API_KEY = ''
#星火认知大模型Spark3.5 Max的domain值,其他版本大模型domain值请前往文档(https://www.xfyun.cn/doc/spark/Web.html)查看
SPARKAI_DOMAIN = 'generalv3.5'

Step3:模型测试

def get_completions(text):
    messages = [ChatMessage(
        role="user",
        content=text
    )]
    spark = ChatSparkLLM(
        spark_api_url=SPARKAI_URL,
        spark_app_id=SPARKAI_APP_ID,
        spark_api_key=SPARKAI_API_KEY,
        spark_api_secret=SPARKAI_API_SECRET,
        spark_llm_domain=SPARKAI_DOMAIN,
        streaming=False,
    )
    handler = ChunkPrintHandler()
    a = spark.generate([messages], callbacks=[handler])
    return a.generations[0][0].text

# 测试模型配置是否正确
text = "你好"
get_completions(text)

这段代码展示了一个通过 ChatSparkLLM 调用大语言模型(LLM)生成文本补全的示例。以下是逐步详细解释:

导入和初始化

假设已经导入了必要的模块和类,例如 ChatMessageChatSparkLLM ChunkPrintHandler

函数定义

def get_completions(text):

定义一个名为 get_completions的函数,接受一个字符串参数 text

创建消息

    messages = [ChatMessage(
        role="user",
        content=text
    )]
  • ChatMessage:创建一个 ChatMessage 对象。
    • role="user":指定消息的角色为用户。
      -content=text:消息内容为传入的 text。
      -messages:将这个消息对象放入一个列表中,传递给模型用于生成回复。

初始化模型


    spark = ChatSparkLLM(
        spark_api_url=SPARKAI_URL,
        spark_app_id=SPARKAI_APP_ID,
        spark_api_key=SPARKAI_API_KEY,
        spark_api_secret=SPARKAI_API_SECRET,
        spark_llm_domain=SPARKAI_DOMAIN,
        streaming=False,
    )
    ```
    
- ChatSparkLLM:初始化一个 ChatSparkLLM 对象。
  - spark_api_url、spark_app_id、spark_api_key、spark_api_secret、spark_llm_domain:这些都是用于访问 SparkAI API 的配置信息。
  - streaming=False:关闭流式输出。

### 回调处理器
```python
    handler = ChunkPrintHandler()
  • ChunkPrintHandler:初始化一个 ChunkPrintHandler 对象,用于处理生成的文本块(chunk)。

生成回复

    a = spark.generate([messages], callbacks=[handler])
  • spark.generate:调用 generate 方法生成回复。
    • [messages]:将消息列表作为参数传入。
    • callbacks=[handler]:使用 ChunkPrintHandler 作为回调处理器,处理生成的文本块。

返回结果

    return a.generations[0][0].text
  • a.generations:获取生成的文本结果。
    • [0][0]:访问第一条生成的回复。
    • .text:获取文本内容。

测试函数

# 测试模型配置是否正确
text = "你好"
get_completions(text)
  • 设置测试文本 text 为 “你好”。
  • 调用 get_completions(text)函数,测试模型是否正确配置,并生成相应的回复。

总结

这段代码展示了如何使用 ChatSparkLLM 通过 SparkAI API 调用大语言模型生成文本补全的过程。包括初始化消息、配置模型参数、设置回调处理器和获取生成的文本。通过这种方式,你可以与大语言模型进行互动,生成基于输入文本的回复。

Step4:数据读取

def read_json(json_file_path):
    """读取json文件"""
    with open(json_file_path, 'r') as f:
        data = json.load(f)
    return data

def write_json(json_file_path, data):
    """写入json文件"""
    with open(json_file_path, 'w') as f:
        json.dump(data, f, ensure_ascii=False, indent=4)

# 读取数据
train_data = read_json("dataset/train.json")
test_data = read_json("dataset/test_data.json")

这段代码包含两个函数,用于读取和写入 JSON 文件,并展示了如何读取训练和测试数据。下面是详细解释每个部分:

1. read_json 函数

def read_json(json_file_path):
    """读取json文件"""
    with open(json_file_path, 'r') as f:
        data = json.load(f)
    return data
功能
  • 目的:读取指定路径的 JSON 文件并返回其内容。
  • 参数
    • json_file_path:JSON 文件的路径。
详细解释
  • with open(json_file_path, 'r') as f
    • 打开指定路径的 JSON 文件,读取模式为 ‘r’(读取)。
    • with 语句用于确保文件在使用完毕后被正确关闭。
  • data = json.load(f)
    • 使用 json 模块的 load 方法将打开的文件对象 f 中的 JSON 数据读取并解析为 Python 对象(通常是字典或列表)。
  • return data
    • 返回读取并解析后的数据。

2. write_json 函数

def write_json(json_file_path, data):
    """写入json文件"""
    with open(json_file_path, 'w') as f:
        json.dump(data, f, ensure_ascii=False, indent=4)
功能
  • 目的:将数据写入指定路径的 JSON 文件。
  • 参数
    • json_file_path:JSON 文件的路径。
    • data:要写入 JSON 文件的数据(通常是字典或列表)。
详细解释
  • with open(json_file_path, 'w') as f
    • 打开指定路径的 JSON 文件,写入模式为 ‘w’(写入)。如果文件不存在,会创建一个新文件。
    • with 语句用于确保文件在使用完毕后被正确关闭。
  • json.dump(data, f, ensure_ascii=False, indent=4)
    • 使用 json 模块的 dump 方法将 data 写入文件对象 f。
    • ensure_ascii=False:确保非 ASCII 字符(如中文)被正确写入,而不转义。
    • indent=4:设置缩进级别为4,使生成的 JSON 文件更易读。

3. 读取数据

# 读取数据
train_data = read_json("dataset/train.json")
test_data = read_json("dataset/test_data.json")
功能
  • 目的:读取训练数据和测试数据。
  • 详细解释
    • 调用 read_json 函数,传入 "dataset/train.json" 路径,读取并返回训练数据,赋值给 train_data
    • 调用 read_json 函数,传入"dataset/test_data.json"路径,读取并返回测试数据,赋值给 test_data

总结

这段代码通过定义 read_jsonwrite_json 两个函数,实现了对 JSON 文件的读取和写入操作。随后,通过调用 read_json 函数读取训练和测试数据,分别存储在 train_datatest_data 变量中。这样可以方便地处理和管理数据集。

Step5:Prompt设计

# prompt 设计
PROMPT_EXTRACT = """
你将获得一段群聊对话记录。你的任务是根据给定的表单格式从对话记录中提取结构化信息。在提取信息时,请确保它与类型信息完全匹配,不要添加任何没有出现在下面模式中的属性。

表单格式如下:
info: Array<Dict(
    "基本信息-姓名": string | "",  // 客户的姓名。
    "基本信息-手机号码": string | "",  // 客户的手机号码。
    "基本信息-邮箱": string | "",  // 客户的电子邮箱地址。
    "基本信息-地区": string | "",  // 客户所在的地区或城市。
    "基本信息-详细地址": string | "",  // 客户的详细地址。
    "基本信息-性别": string | "",  // 客户的性别。
    "基本信息-年龄": string | "",  // 客户的年龄。
    "基本信息-生日": string | "",  // 客户的生日。
    "咨询类型": string[] | [],  // 客户的咨询类型,如询价、答疑等。
    "意向产品": string[] | [],  // 客户感兴趣的产品。
    "购买异议点": string[] | [],  // 客户在购买过程中提出的异议或问题。
    "客户预算-预算是否充足": string | "",  // 客户的预算是否充足。示例:充足, 不充足
    "客户预算-总体预算金额": string | "",  // 客户的总体预算金额。
    "客户预算-预算明细": string | "",  // 客户预算的具体明细。
    "竞品信息": string | "",  // 竞争对手的信息。
    "客户是否有意向": string | "",  // 客户是否有购买意向。示例:有意向, 无意向
    "客户是否有卡点": string | "",  // 客户在购买过程中是否遇到阻碍或卡点。示例:有卡点, 无卡点
    "客户购买阶段": string | "",  // 客户当前的购买阶段,如合同中、方案交流等。
    "下一步跟进计划-参与人": string[] | [],  // 下一步跟进计划中涉及的人员(客服人员)。
    "下一步跟进计划-时间点": string | "",  // 下一步跟进的时间点。
    "下一步跟进计划-具体事项": string | ""  // 下一步需要进行的具体事项。
)>

请分析以下群聊对话记录,并根据上述格式提取信息:

**对话记录:**

{content}


请将提取的信息以JSON格式输出。
不要添加任何澄清信息。
输出必须遵循上面的模式。
不要添加任何没有出现在模式中的附加字段。
不要随意删除字段。

**输出:**

[{{
    "基本信息-姓名": "姓名",
    "基本信息-手机号码": "手机号码",
    "基本信息-邮箱": "邮箱",
    "基本信息-地区": "地区",
    "基本信息-详细地址": "详细地址",
    "基本信息-性别": "性别",
    "基本信息-年龄": "年龄",
    "基本信息-生日": "生日",
    "咨询类型": ["咨询类型"],
    "意向产品": ["意向产品"],
    "购买异议点": ["购买异议点"],
    "客户预算-预算是否充足": "充足或不充足",
    "客户预算-总体预算金额": "总体预算金额",
    "客户预算-预算明细": "预算明细",
    "竞品信息": "竞品信息",
    "客户是否有意向": "有意向或无意向",
    "客户是否有卡点": "有卡点或无卡点",
    "客户购买阶段": "购买阶段",
    "下一步跟进计划-参与人": ["跟进计划参与人"],
    "下一步跟进计划-时间点": "跟进计划时间点",
    "下一步跟进计划-具体事项": "跟进计划具体事项"
}}, ...]

"""

这个 PROMPT_EXTRACT 设计旨在从对话记录中提取结构化信息,以便进一步处理或分析。以下是对每个部分的详细解释:

模板设计

表单格式
info: Array<Dict(
    "基本信息-姓名": string | "",  // 客户的姓名。
    "基本信息-手机号码": string | "",  // 客户的手机号码。
    "基本信息-邮箱": string | "",  // 客户的电子邮箱地址。
    "基本信息-地区": string | "",  // 客户所在的地区或城市。
    "基本信息-详细地址": string | "",  // 客户的详细地址。
    "基本信息-性别": string | "",  // 客户的性别。
    "基本信息-年龄": string | "",  // 客户的年龄。
    "基本信息-生日": string | "",  // 客户的生日。
    "咨询类型": string[] | [],  // 客户的咨询类型,如询价、答疑等。
    "意向产品": string[] | [],  // 客户感兴趣的产品。
    "购买异议点": string[] | [],  // 客户在购买过程中提出的异议或问题。
    "客户预算-预算是否充足": string | "",  // 客户的预算是否充足。示例:充足, 不充足
    "客户预算-总体预算金额": string | "",  // 客户的总体预算金额。
    "客户预算-预算明细": string | "",  // 客户预算的具体明细。
    "竞品信息": string | "",  // 竞争对手的信息。
    "客户是否有意向": string | "",  // 客户是否有购买意向。示例:有意向, 无意向
    "客户是否有卡点": string | "",  // 客户在购买过程中是否遇到阻碍或卡点。示例:有卡点, 无卡点
    "客户购买阶段": string | "",  // 客户当前的购买阶段,如合同中、方案交流等。
    "下一步跟进计划-参与人": string[] | [],  // 下一步跟进计划中涉及的人员(客服人员)。
    "下一步跟进计划-时间点": string | "",  // 下一步跟进的时间点。
    "下一步跟进计划-具体事项": string | ""  // 下一步需要进行的具体事项。
)>
对话记录解析

对话记录通常包含多位参与者的交谈。我们需要从中提取出有关客户的详细信息。这些信息可以是显式提供的,如客户直接提供的姓名、电话等,也可以是隐式提到的,如通过对话推断出的意向产品、预算信息等。

示例解析

假设我们有以下对话记录:
客户A:你好,我想了解一下你们的产品。
客服B:您好,请问您贵姓?
客户A:我姓张,张三。
客服B:好的,张先生,请问您的联系电话是?
客户A:我的手机号是12345678901。
客服B:感谢,请问您的邮箱地址?
客户A:zhangsan@example.com。
客服B:好的,请问您主要关注的是哪些产品呢?
客户A:我对你们的智能手机和笔记本比较感兴趣。
客服B:请问您对产品的预算是多少?
客户A:我预计在5000-8000元之间。

输出格式

我们需要提取并结构化这些信息,如下所示:

[
    {
        "基本信息-姓名": "张三",
        "基本信息-手机号码": "12345678901",
        "基本信息-邮箱": "zhangsan@example.com",
        "基本信息-地区": "",
        "基本信息-详细地址": "",
        "基本信息-性别": "男",
        "基本信息-年龄": "",
        "基本信息-生日": "",
        "咨询类型": ["询价"],
        "意向产品": ["智能手机", "笔记本"],
        "购买异议点": [],
        "客户预算-预算是否充足": "充足",
        "客户预算-总体预算金额": "5000-8000元",
        "客户预算-预算明细": "",
        "竞品信息": "",
        "客户是否有意向": "有意向",
        "客户是否有卡点": "无卡点",
        "客户购买阶段": "咨询中",
        "下一步跟进计划-参与人": ["客服B"],
        "下一步跟进计划-时间点": "",
        "下一步跟进计划-具体事项": "进一步了解客户需求并提供产品详情"
    }
]

PROMPT_EXTRACT 的作用

  • 输入格式化:提供统一的输入格式,确保模型知道如何解析输入的对话记录。
  • 信息提取:指示模型从对话记录中提取特定的信息,并以预定义的格式返回。
  • 输出验证:确保生成的输出遵循预定义的 JSON 格式,不包含多余的信息或字段。

通过这种设计,模型可以高效地从群聊对话中提取结构化的信息,方便后续的数据分析和处理。

Step6:主函数启动

import json

class JsonFormatError(Exception):
"""
JsonFormatError:定义一个自定义异常类,用于在 JSON 格式不符合预期时抛出异常
"""
    def __init__(self, message):
        self.message = message
        super().__init__(self.message)

def convert_all_json_in_text_to_dict(text):
    """提取LLM输出文本中的json字符串
    convert_all_json_in_text_to_dict:遍历输入文本,提取 JSON 格式的子字符串并转换为 Python 字典。
使用栈(stack)来追踪 JSON 字符串的起始和结束位置,从而准确提取完整的 JSON 对象。"""
    dicts, stack = [], []
    for i in range(len(text)):
        if text[i] == '{':
            stack.append(i)
        elif text[i] == '}':
            begin = stack.pop()
            if not stack:
                dicts.append(json.loads(text[begin:i+1]))
    return dicts

# 查看对话标签
def print_json_format(data):
    """格式化输出json格式
    print_json_format:将输入的 Python 对象格式化为 JSON 字符串并打印,便于阅读。"""
    print(json.dumps(data, indent=4, ensure_ascii=False))



def check_and_complete_json_format(data):
"""
check_and_complete_json_format:检查 JSON 数据是否符合预期的格式,
缺失的键用默认值补全,不符合类型的键抛出异常。
使用 required_keys 字典定义所需的键及其预期类型。
"""
"""
required_keys 字典:定义一个字典,包含所有预期的键及其对应的值类型。
例如,"基本信息-姓名" 的值应该是字符串 (str),而 "咨询类型" 的值应该是列表 (list)。
"""
    required_keys = {
        "基本信息-姓名": str,
        "基本信息-手机号码": str,
        "基本信息-邮箱": str,
        "基本信息-地区": str,
        "基本信息-详细地址": str,
        "基本信息-性别": str,
        "基本信息-年龄": str,
        "基本信息-生日": str,
        "咨询类型": list,
        "意向产品": list,
        "购买异议点": list,
        "客户预算-预算是否充足": str,
        "客户预算-总体预算金额": str,
        "客户预算-预算明细": str,
        "竞品信息": str,
        "客户是否有意向": str,
        "客户是否有卡点": str,
        "客户购买阶段": str,
        "下一步跟进计划-参与人": list,
        "下一步跟进计划-时间点": str,
        "下一步跟进计划-具体事项": str
    }

    if not isinstance(data, list):
    """
    数据类型检查:检查输入的 data 是否为列表 (list) 类型。
    如果不是,抛出 JsonFormatError 异常,提示 "Data is not a list"。
    """
        raise JsonFormatError("Data is not a list")

    for item in data:
        if not isinstance(item, dict):
            raise JsonFormatError("Item is not a dictionary")
            """
            遍历 data 列表:逐个检查列表中的每个 item。
            类型检查:检查每个 item 是否为字典 (dict) 类型。
            如果不是,抛出 JsonFormatError 异常,提示 "Item is not a dictionary"。
            """
            
        for key, value_type in required_keys.items():
        """
        遍历 required_keys 字典:逐个检查每个预期的键。
            键存在性检查和补全:如果 item 中缺少某个键,则根据该键的预期类型补全其默认值:
            如果类型是 list,则补全为空列表 ([])。
            否则,补全为空字符串 ("")。
            类型检查:检查 item 中的每个键值是否符合预期的类型。
            如果不符合,抛出 JsonFormatError 异常,提示具体的键和类型。
            列表内部元素类型检查:如果键的值类型是 list,
            进一步检查列表中的所有元素是否都是字符串 (str) 类型。
            如果不是,抛出 JsonFormatError 异常,提示该键不包含全部字符串。
            """
            if key not in item:
                item[key] = [] if value_type == list else ""
            if not isinstance(item[key], value_type):
                raise JsonFormatError(f"Key '{key}' is not of type {value_type.__name__}")
            if value_type == list and not all(isinstance(i, str) for i in item[key]):
                raise JsonFormatError(f"Key '{key}' does not contain all strings in the list")

    return data # 返回经过检查和补全的 data 列表。
   
from tqdm import tqdm

retry_count = 5 # 设置重试次数以应对可能的错误。
result = []
error_data = []
# result 和 error_data:分别用于存储成功和失败的数据。

for index, data in tqdm(enumerate(test_data)):
"""
在遍历 test_data 时:
调用 get_completions 获取模型输出。
提取 JSON 数据并检查格式。
处理过程中如果发生错误,记录并继续处理其他数据。
"""
   index += 1
   # index:由于 enumerate 从 0 开始,给索引加1以使其从 1 开始,更符合常规习惯。
   is_success = False
   # is_success:标记处理是否成功,初始设为 False。
   for i in range(retry_count):
   # 重试循环:尝试最多 retry_count 次,以应对可能的错误。
   """
   try 块:尝试执行以下操作:
       调用 get_completions 函数,使用 PROMPT_EXTRACT 模板生成 data["chat_text"] 的补全结果。
       调用 convert_all_json_in_text_to_dict 函数,将生成的文本结果转换为 JSON 格式的 Python 字典。
       调用 check_and_complete_json_format 函数,检查并补全 JSON 格式,确保其符合预期。
       将处理成功的结果追加到 result 列表中,并记录当前的 index。
       设置 is_success 为 True,并跳出重试循环。
   except 块:如果发生任何异常,捕获并打印错误信息,继续重试。
   """
       try:
           res = get_completions(PROMPT_EXTRACT.format(content=data["chat_text"]))
           infos = convert_all_json_in_text_to_dict(res)
           infos = check_and_complete_json_format(infos)
           result.append({
               "infos": infos,
               "index": index
           })
           is_success = True
           break
       except Exception as e:
           print("index:", index, ", error:", e)
           continue
           
   if not is_success:
   """
   is_success 检查:如果在所有重试次数内仍然未能成功处理当前数据项,将其标记为失败。
   记录失败数据:在 data 中添加 index 键,并将其追加到 error_data 列表中。
   """
       data["index"] = index
       error_data.append(data)
# 故障数据处理
"""这段代码展示了如何处理在初次尝试时未能成功处理的数据"""

if error_data:
"""如果有未成功处理的数据,进行多次重试,直至所有数据处理完成
检查 error_data 是否为空:只有在初次处理过程中存在未成功的数据时才执行重试逻辑。
设置 retry_count:将重试次数设为 10 次。
初始化 error_data_temp:用于暂存每轮重试后仍未成功的数据。
"""
    retry_count = 10 # 重试次数
    error_data_temp = []
    
    
    while True:
    """
    无限循环 while True:直到 error_data_temp 为空时才跳出循环。
    重试数据交换:如果 error_data_temp 中有数据,则将其赋值给 error_data,并清空 error_data_temp。
    """
        if error_data_temp:
            error_data = error_data_temp
            error_data_temp = []
            
            
            
        for data in tqdm(error_data):
        """
        遍历 error_data:使用 tqdm 显示进度条,逐个处理未成功的数据。
        初始化 is_success:标记每个数据项的处理状态。
        重试处理逻辑:
        调用 get_completions:使用 PROMPT_EXTRACT 模板和数据项的 chat_text 调用大语言模型。
        转换和检查 JSON 数据:将模型输出转换为 JSON 格式,并检查其格式。
        追加成功处理的数据:如果处理成功,将结果追加到 result 列表,并设置 is_success 为 True,
        然后跳出重试循环。
        捕获异常:如果发生异常,打印错误信息,继续重试。
        记录未成功的数据:如果在重试次数内未能成功处理,将数据项追加到 error_data_temp。
        """
            is_success = False
            for i in range(retry_count):
                try:
                    res = get_completions(PROMPT_EXTRACT.format(content=data["chat_text"]))
                    infos = convert_all_json_in_text_to_dict(res)
                    infos = check_and_complete_json_format(infos)
                    result.append({
                        "infos": infos,
                        "index": data["index"]
                    })
                    is_success = True
                    break
                except Exception as e:
                    print("index:", index, ", error:", e)
                    continue
                    
            if not is_success:
                error_data_temp.append(data)
                
                
        if not error_data_temp:
        """
        检查 error_data_temp:如果 error_data_temp 为空,说明所有数据项均已成功处理,跳出无限循环。
        排序结果:按 index 对 result 列表进行排序,确保结果按原始数据顺序排列。
        """
            break
            
    result = sorted(result, key=lambda x: x["index"])

Step7:生成提交文件

# 保存输出
write_json("output.json", result)

二、构思idea,改进baseline

【构思】在构思笔记层中写下即时想法或大纲,保留自己的idea尝试过程。

三、封存思想精华,个人感悟,学习记录都ok

【整理】

这个夏令营不简单 #AI夏令营 #Datawhale #夏令营

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值