调用模型| 豆包MarsCode AI 刷题

227 阅读9分钟

通过HuggingFace调用Llama

# 导入必要的库
from transformers import AutoTokenizer, AutoModelForCausalLM

# 加载预训练模型的分词器
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-chat-hf")

# 加载预训练的模型
# 使用 device_map 参数将模型自动加载到可用的硬件设备上,例如GPU
model = AutoModelForCausalLM.from_pretrained(
          "meta-llama/Llama-2-7b-chat-hf", 
          device_map = 'auto')  

# 定义一个提示,希望模型基于此提示生成故事
prompt = "请给我讲个春天的爱情故事。"

# 使用分词器将提示转化为模型可以理解的格式,并将其移动到GPU上
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")

# 使用模型生成文本,设置最大生成令牌数为2000
outputs = model.generate(inputs["input_ids"], max_new_tokens=2000)

# 将生成的令牌解码成文本,并跳过任何特殊的令牌,例如[CLS], [SEP]等
response = tokenizer.decode(outputs[0], skip_special_tokens=True)

# 打印生成的响应
print(response)
  • 导入AutoTokenizer:这是一个用于自动加载预训练模型的相关分词器的工具。分词器负责将文本转化为模型可以理解的数字格式。
  • 导入AutoModelForCausalLM:这是用于加载因果语言模型(用于文本生成)的工具。
  • 使用from_pretrained方法来加载预训练的分词器和模型。其中,device_map = 'auto' 是为了自动地将模型加载到可用的设备上,例如GPU。
  • 然后,给定一个提示(prompt):"请给我讲个玫瑰的爱情故事?",并使用分词器将该提示转换为模型可以接受的格式,return_tensors="pt" 表示返回PyTorch张量。语句中的 .to("cuda") 是GPU设备格式转换,因为我在GPU上跑程序,不用这个的话会报错,如果你使用CPU,可以试一下删掉它。
  • 最后使用模型的 .generate() 方法生成响应。max_new_tokens=2000 限制生成的文本的长度。使用分词器的 .decode() 方法将输出的数字转化回文本,并且跳过任何特殊的标记。

LangChain和HuggingFace的接口

通过 HuggingFace Hub

# 导入HuggingFace API Token
import os
os.environ['HUGGINGFACEHUB_API_TOKEN'] = '你的HuggingFace API Token'

# 导入必要的库
from langchain import PromptTemplate, HuggingFaceHub, LLMChain

# 初始化HF LLM
llm = HuggingFaceHub(
    repo_id="google/flan-t5-small",
    #repo_id="meta-llama/Llama-2-7b-chat-hf",
)

# 创建简单的question-answering提示模板
template = """Question: {question}
              Answer: """

# 创建Prompt          
prompt = PromptTemplate(template=template, input_variables=["question"])

# 调用LLM Chain --- 我们以后会详细讲LLM Chain
llm_chain = LLMChain(
    prompt=prompt,
    llm=llm
)

# 准备问题
question = "Rose is which type of flower?"

# 调用模型并返回结果
print(llm_chain.run(question))

通过 HuggingFace Pileline

# 指定预训练模型的名称
model = "meta-llama/Llama-2-7b-chat-hf"

# 从预训练模型中加载词汇器
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained(model)

# 创建一个文本生成的管道
import transformers
import torch
pipeline = transformers.pipeline(
    "text-generation",
    model=model,
    torch_dtype=torch.float16,
    device_map="auto",
    max_length = 1000
)

# 创建HuggingFacePipeline实例
from langchain import HuggingFacePipeline
llm = HuggingFacePipeline(pipeline = pipeline, 
                          model_kwargs = {'temperature':0})

# 定义输入模板,该模板用于生成花束的描述
template = """
              为以下的花束生成一个详细且吸引人的描述:
              花束的详细信息:
              ```{flower_details}```
           """

# 使用模板创建提示
from langchain import PromptTemplate,  LLMChain
prompt = PromptTemplate(template=template, 
                     input_variables=["flower_details"])

# 创建LLMChain实例
from langchain import PromptTemplate
llm_chain = LLMChain(prompt=prompt, llm=llm)

# 需要生成描述的花束的详细信息
flower_details = "12支红玫瑰,搭配白色满天星和绿叶,包装在浪漫的红色纸中。"

# 打印生成的花束描述
print(llm_chain.run(flower_details))

简要了解transformers pipeline的配置参数:

image.png

用 LangChain 调用自定义语言模型

# 导入需要的库
from llama_cpp import Llama
from typing import Optional, List, Mapping, Any
from langchain.llms.base import LLM

# 模型的名称和路径常量
MODEL_NAME = 'llama-2-7b-chat.ggmlv3.q4_K_S.bin'
MODEL_PATH = '/home/huangj/03_Llama/'

# 自定义的LLM类,继承自基础LLM类
class CustomLLM(LLM):
    model_name = MODEL_NAME

    # 该方法使用Llama库调用模型生成回复
    def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
        prompt_length = len(prompt) + 5
        # 初始化Llama模型,指定模型路径和线程数
        llm = Llama(model_path=MODEL_PATH+MODEL_NAME, n_threads=4)
        # 使用Llama模型生成回复
        response = llm(f"Q: {prompt} A: ", max_tokens=256)
        
        # 从返回的回复中提取文本部分
        output = response['choices'][0]['text'].replace('A: ', '').strip()

        # 返回生成的回复,同时剔除了问题部分和额外字符
        return output[prompt_length:]

    # 返回模型的标识参数,这里只是返回模型的名称
    @property
    def _identifying_params(self) -> Mapping[str, Any]:
        return {"name_of_model": self.model_name}

    # 返回模型的类型,这里是"custom"
    @property
    def _llm_type(self) -> str:
        return "custom"
    

# 初始化自定义LLM类
llm = CustomLLM()

# 使用自定义LLM生成一个回复
result = llm("昨天有一个客户抱怨他买了花给女朋友之后,两天花就枯了,你说作为客服我应该怎么解释?")

# 打印生成的回复
print(result)

必须实现_call函数,用于接受输入字符串并返回响应字符串。

思考题

  1. 现在请你再回答一下,什么时候应该使用OpenAI的API?什么时候应该使用开源模型?或者自己开发/微调的模型?

    • OpenAI API:适合快速开发、无需复杂部署、专注于核心应用的场景。优点是高性能、可扩展性和持续优化,适合处理大规模的 NLP 任务,但无法保证隐私安全。

      • 对模型通用性能、精度等要求高
    • 开源模型:适合有一定技术背景的团队,拥有部署环境,或对模型调试有需求的情况。优点是自由度高,成本低,适合小团队或对隐私要求较高的项目。

      • 私有数据
      • 对特定任务需要修改模型的架构
    • 自定义/微调模型:适合有特定任务需求、数据量大且拥有开发资源的场景。优点是模型定制化程度高,能高度匹配业务需求。

      • 业务场景过于复杂
      • 模型架构完全不能复用
  2. 请你使用HuggingFace的Transformers库,下载新的模型进行推理,比较它们的性能。

    from transformers import AutoTokenizer, AutoModelForCausalLM
    import time,torch
    
    device=torch.device("cuda:7")
    
    def test_model_performance(model_name, prompt, max_tokens=200):
        tokenizer = AutoTokenizer.from_pretrained(model_name)
        model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto").to(device)
    
        inputs = tokenizer(prompt, return_tensors="pt").to(device)
    
        start_time = time.time()
        outputs = model.generate(inputs["input_ids"], max_new_tokens=max_tokens)
        end_time = time.time()
    
        response = tokenizer.decode(outputs[0], skip_special_tokens=True)
        print(f"Model: {model_name}\nResponse: {response}\nTime Taken: {end_time - start_time} seconds\n")
    
    prompt = "请给我讲个春天的爱情故事。"
    models_to_test = ["openai-community/gpt2", "meta-llama/Llama-2-7b-chat-hf"]
    
    for model_name in models_to_test:
        model_name="local_models/huggingface/"+model_name
        test_model_performance(model_name, prompt)
    

    输出:

    Model: /data/zoudq/local_models/huggingface/openai-community/gpt2
    Response: 请给我讲个春天的爱情故事。
    
    爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事爱情故事
    Time Taken: 3.056500196456909 seconds
    
    Model: /data/zoudq/local_models/huggingface/bigscience/bloomz-560m
    Response: 请给我讲个春天的爱情故事。 故事是这样的: 一个女孩在街上散步,突然发现一个男人正在偷她的自行车。女孩很生气,就跑去追赶他。 男人很着急,就冲她喊: “你偷了我的自行车,我要马上把你送走。” 女孩很生
    气,就跑去追赶他。 男人很着急,就冲她喊: “你偷了我的自行车,我要马上把你送走。” 女孩很生气,就跑去追赶他。 男人很着急,就冲她喊: “你偷了我的自行车,我要马上把你送走。” 女孩很生气,就跑去追赶他。 男人很着急,就冲她喊: “你偷了我的自行车,我要马上把你送走。” 女孩很生气,就跑去追赶他。 男人很着急,就冲她喊: “你偷了我的自行车,我要马上把你送走。” 女孩很生气,就跑
    Time Taken: 3.7783100605010986 seconds
    

    560m还是比124m更给力,但还是半斤八两,都差不多。

  3. 请你在LangChain中,使用HuggingFaceHub和HuggingFace Pipeline这两种接口,调用当前最流行的大语言模型。

    HuggingFaceHub

    # HuggingFaceHub
    from langchain.prompts import PromptTemplate
    from langchain.chains import LLMChain
    from langchain_community.llms import HuggingFaceEndpoint
    from langchain_huggingface import HuggingFaceEndpoin
    
    llm=HuggingFaceEndpoint(
        repo_id="bigscience/bloomz-560m"
    )
    
    template="请给我讲个{object}的爱情故事。"
    prompt=PromptTemplate.from_template(template)
    
    llm_chain=prompt|llm
    
    print(llm_chain.invoke("秋天"))
    

    一直访问超时,所以没有输出。

    HuggingFace Pipeline

    # HuggingFace Pipeline
    import transformers
    import torch
    from langchain import HuggingFacePipeline
    
    pipeline=transformers.pipeline(
        'text-generation', 
        model=model_path,
        torch_type=torch.float16,
        device_map="auto",
        max_length=1000
    )
    
    llm=HuggingFacePipeline(
        pipeline=pipeline,
        model_kwargs={'temperature':0}
    )
    
    llm_chain=LLMChain(
        llm=llm,
        prompt=prompt
    )
    
    print(llm_chain.run("冬天"))
    

    输出:

    请给我讲个冬天的爱情故事。 故事发生在一个寒冷的冬日。 故事主要讲述了两个女孩,一个叫伊丽莎白,一个叫艾丽莎。 伊丽莎白是美丽的,聪明,有抱负的,她喜欢上了一个叫艾丽莎的男孩。伊丽莎白和艾丽莎都喜欢上了艾丽莎。伊丽莎白和艾丽莎都想和艾
    丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽
    莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想
    和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。伊丽莎白和艾丽莎都想和艾丽莎在一起。
    

    看来 bloomz-560m 还是模型太小了,会有长文本重复的问题。