所有tag为书生大模型的文档,连同本文档在内,为书生大模型实战营训练内容,文档中的内容并不局限于实战营本身,但算力平台均首选上海AI实验室开发的云端集成开发环境InternStudio开发手册InternStudio算力平台的相关内容可以点击链接跳转。

1.Conda与Pip

1.1 Conda虚拟环境

虚拟环境是Python开发中不可或缺的一部分,它允许你在不同的项目中使用不同版本的库,避免依赖冲突。Conda是一个强大的包管理器和环境管理器。在大模型实战营-L0-01-Linux基础中已经描述过Conda的相关内容。本篇文章将会实际创建和使用新的虚拟环境。

那么首先,需要在Vscode上通过SSH连接到算力平台,详细步骤在刚刚的文章中也有提及,此处不再赘述:

image
image

平台已经预装好了Conda,可以在终端直接创建名为ArtemisYi_Python的虚拟环境。

conda create -n ArtemisYi_Python python=3.10

创建完成后,可以用下述命令查看现有的环境,然后进行激活。

conda env list
conda activate ArtemisYi_Python

现在已经成功激活了刚刚新创建的ArtemisYi_Python环境。

image
image

如果想要删除已经创建的环境,先要退出那个环境,然后再删除环境:

conda deactivate
conda env remove -n ArtemisYi_Python
image
image

但是现在还是需要一个虚拟环境的,需要重新创建一个。但这次可以选择安装到指定目录下。

如果想把Conda环境安装到指定的目录,只需要在创建时使用--prefix参数,直接在该路径下。在激活指定目录下的环境时,则是直接将环境名称替换成所在文件夹的名称即可。

conda create --prefix /root/ArtemisYi_Python python=3.10
conda activate /root/ArtemisYi_Python
image
image

此时,在确保VS Code安装了Python并且确定了解释器之后,就可以写简单的Python代码了,这里展示Leetcode 383:

image
image

1.2 使用pip安装Python三方依赖包

激活环境后,就可以尝试安装新的包了。在Python开发中,安装和管理第三方包是日常任务。pip是Python官方的包管理工具,全称为“Python Package Installer”,用于方便地安装、升级和管理Python包。

pip install <somepackage> # 安装单个包,<somepackage>替换成你要安装的包名
pip install pandas numpy # 安装多个包,如panda和numpy
pip install numpy==2.0 # 指定版本安装
pip install numpy>=1.19,<2.0 # 使用版本范围安装
pip install <somepackage> --target /your_path # 指定安装目录

举个例子,可以安装一个常用的库pandas

image
image

在使用pip的时候可以使用--target-t参数来指定安装目录,此时pip会将你需要安装的包安装到你指定的目录下。此处可以创建一个新的文件夹pkgs,然后把1.21版本的numpy安装进去:

image
image

这里报错是因为有版本冲突,但是实际上本文并不需要使用pandas,因此忽略。

然后再把当前环境里的numpy删掉:

pip uninstall numpy

现在当前环境里已经没有numpy包了,但可以调用先前创建的pkgs目录里的numpy

下面用numpy写一个简单的py文件:

import sys  
  
# 你要添加的目录路径  
your_directory = '/root/pkgs'  
  
# 检查该目录是否已经在 sys.path 中  
if your_directory not in sys.path:  
    # 将目录添加到 sys.path  
    sys.path.append(your_directory)  

import numpy as np

# 生成一个随机的3x3矩阵
matrix = np.random.rand(3, 3)
print("生成的随机矩阵:")
print(matrix)

# 计算行列式
det = np.linalg.det(matrix)
print("\n矩阵的行列式:")
print(det)

# 计算逆矩阵(如果行列式不为0)
if det != 0:
    inverse = np.linalg.inv(matrix)
    print("\n矩阵的逆矩阵:")
    print(inverse)
else:
    print("\n矩阵不可逆,因为行列式为0。")

# 计算特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(matrix)
print("\n矩阵的特征值:")
print(eigenvalues)
print("\n矩阵的特征向量:")
print(eigenvectors)
image
image

由于当前环境里的numpy已经被删除了,因此VS Code直接发出了警告;但是通过手动添加,在程序运行时,仍然可以调用手动安装的numpy包。值得注意的是,import numpy as np这一句得放在目录添加后,否则还是会识别不到。

2.使用VS Code进行Python debug

2.1 什么是debug?

当刚开始学习Python编程时,可能会遇到代码不按预期运行的情况。这时,就需要用到“debug”了。简单来说,“debug”就是能再程序中设置中断点并支持一行一行地运行代码,观测程序中变量的变化,然后找出并修正代码中的错误。而VS Code提供了一个非常方便的debug工具,可以帮助程序员更容易地找到和修复错误。

在debug过程中,断点允许程序员在程序的执行流程中设置暂停点。当程序运行到这些断点时,执行会暂时中断,使得我们可以检查此时程序的状态,包括变量的值、内存的内容等。断点为我们提供了一个观察程序运行细节的机会,从而帮助我们定位和解决程序中的错误或问题。

2.2 使用VS Code进行Python debug的流程

2.2.1 断点

这里提供一个简单的python文件用于体验debug的整个流程:

def range_sum(start,end):
    sum_res = 0
    for i in range(start,end):
        sum_res+=i
    return sum_res

if __name__ =="__main__":
    print(range_sum(1,10))

在文件中,点击左侧活动栏里的”运行和调试”图标。

image
image

选择python debugger后选择“Python File” config。

image
image

可以直接编辑生成的launch.json文件,配置调试参数,比如添加config(Add Configuration)等。

在代码行号旁边点击,可以添加一个红点,这就是断点(如果不能添加红点需要检查一下Python扩展是否已经正确安装)。当代码运行到这里时,它会停下来,这样就可以检查变量的值、执行步骤等。

接下来,在第4行的核心代码处打上断点,然后开始调试:

image
image

接下来就可以检查和修改变量的值了

image
image

由于断点处于第四行,循环还未开始,因此目前各个变量还没有什么变化。

此时,可以使用顶部的debug面板的按钮来单步执行代码。这样可以逐行运行代码,并查看每行代码执行后的效果。

image
image

debug面板各按钮功能介绍:

  • continue: 继续运行到下一个断点。
  • step over: 单步跳过,可以理解为运行当前行代码,不进入具体的函数或者方法。
  • step into: 单步进入。如果当前行代码存在函数调用,则进入该函数内部。如果当前行代码没有函数调用,则等价于step over
  • step out: 单步退出函数,返回到调用该函数的上一层代码。
  • restart: 重新启动调试。
  • stop: 终止调试。

如果你找到了代码中的错误,可以修复它,然后重新运行debug来确保问题已经被解决。

通过遵循以上步骤,你可以使用VS Code的debug功能来更容易地找到和修复你Python代码中的错误。可以自己编写一个简单的python脚本,并尝试使用debug来更好的理解代码的运行逻辑。

记住,debug是编程中非常重要的一部分,所以不要怕花时间在这上面。随着时间的推移,你会变得越来越擅长它!

2.2.2 不同的断点

在VS Code中,可以设置各种不同的断点,只有在满足特定条件时才会触发。

  • 普通断点:在代码行号左侧点击,添加断点。
  • 条件断点:在断点标记上右键,选择条件断点(conditional breakpoint)。VS Code 中常用的条件断点主要有三种类型:
    • 表达式(Expression):输入一个 Python 表达式,每次触发断点时运行该表达式,当表达式的值为 True 时 VS Code 会暂停执行。例如:x == 10
    • 触发计数(Hit Count):断点触发计数达到输入值时才会暂停运行。
    • 记录日志(Log Message):触发该断点时在 Debug Console 中输出指定信息,实际上就是 logpoint。需要输入要输出的信息,如果要用到表达式,可以使用 {} 将表达式括起来。例如,每次记录变量 i 的值可以写 x={i}

比如,想让代码在 i=end-2 时停下来,可以这样设置:

在断点处右键选择“条件断点”,然后输入条件 i == end-1,然后运行debug:

image
image

可以看到程序在i=8时停了下来,并且可以检查各个变量的值。

再比如,如果想让sum_res+=i在运行5次后停下来,可以在该行设置Hit count断点为5。

image
image

那么程序就会在i=5时停下来。

如果想要记录i在整个流程中的变化,可以设置日志记录断点,并在调试控制台检查:

image
image

2.3 Vs Code命令行debug

很多时候要debug的不止是一个简单的python文件,而是很多参数,参数中不止会有简单的值还可能有错综复杂的文件关系,甚至debug一整个项目。这种情况下,直接使用命令行来发起debug会是一个更好的选择。

仍然以原先的demo为例,先删除原有的launch.json然后重新创建一个新的(也可以在原有的基础上选择添加配置),并在选择调试器时选择远程附加,添加服务器和端口号:

image
image
image
image

接下来在当前环境安装debugpy

pip install debugpy

然后就可以在命令行中发起debug了:

python -m debugpy --listen 5678 --wait-for-client ./demo.py
  • ./myscript.py可以替换为想要debug的python文件,后面可以和直接在命令行中启动python一样跟上输入的参数。记得要先在想要debug的python文件打好断点并保存。
  • -wait-for-client参数会让debug server在等客户端连入后才开始运行debug。在这就是要等到run and debug界面启动debug。

先在终端中发起debug server,然后再去vscode debug页面单击一下绿色箭头开启debug。

image
image

接下来的操作就和上面一样了。

这边有个不方便的地方,python -m debugpy --listen 5678 --wait-for-client这个命令太长了,每次都打很麻烦,因此可以给这段常用的命令设置一个别名。

linux系统中,可以对 ~/.bashrc 文件中添加以下命令:

alias pyd='python -m debugpy --wait-for-client --listen 5678' # 设置别名
source ~/.bashrc # 初始化一下

这样之后使用 pyd 命令(你可以自己命名) 替代 python 就能在命令行中起debug了,之前的debug命令就变成了

pyd ./myscript.py

3.Python调用InternLM大模型

3.1 获取api key

前往书生浦语的API文档,登陆后点击API tokens。

基本上所有主流的大模型都需要去官网申请并调用api。不同的大模型价格不同,当前市场最贵的应该还是OpenAI的。

image
image

这里一定要注意,浦语的token只有刚创建的时候才能看到全文,后续没法再查看已经创建好的token,如果忘记需要重新创建,所以创建完了以后记得先复制保存到本地。

image
image

然后,在创建的环境中安装大名鼎鼎的OpenAI的库:

pip install openai

安装完毕后,在终端临时将api加入变量,此时该环境变量只在当前终端内有效。

export api_key="填入你的api token"

然后,创建Python文件,就可以尝试调用InternLM了:

#./LLM_demo.py
from openai import OpenAI
import os
def internlm_gen(prompt,client):
    '''
    LLM生成函数
    Param prompt: prompt string
    Param client: OpenAI client 
    '''
    response = client.chat.completions.create(
        model="internlm2.5-latest",
        messages=[
            {"role": "user", "content": prompt},
      ],
        stream=False
    )
    return response.choices[0].message.content

api_key = os.getenv('api_key')
#api_key = "" #也可以明文写在代码内,不推荐
client = OpenAI(base_url="https://internlm-chat.intern-ai.org.cn/puyu/api/v1/",api_key=api_key)
prompt = '''你好!你是谁?'''
response = internlm_gen(prompt,client)
print(response)
python LLM_demo.py

最终可以在终端里得到大模型的回复:

image
image
你好!我是书生·浦语(InternLM),是由上海人工智能实验室开发的一款语言模型。我致力于提供帮助、诚实且无害的交流。无论你选择使用中文还是英文,我都能进行流畅的沟通和理解。有什么我可以帮助你的吗?

至此,调用大模型api并得到回复的任务已经完成了。但是如果切换终端就需要输入一次api非常麻烦,因此若是想永久加入环境变量,可以对 ~/.bashrc 文件中添加以下命令:

export api_key="填入你的api"
image
image
source ~/.bashrc

现在,每次启动终端时就不需要再输入api了。

3.2 简单的debug

下面是一段调用书生浦语API实现将非结构化文本转化成结构化json的例子,其中有一个小bug会导致报错。

from openai import OpenAI
import os
import json
def internlm_gen(prompt,client):
    '''
    LLM生成函数
    Param prompt: prompt string
    Param client: OpenAI client 
    '''
    response = client.chat.completions.create(
        model="internlm2.5-latest",
        messages=[
            {"role": "user", "content": prompt},
      ],
        stream=False
    )
    return response.choices[0].message.content

api_key = os.getenv('api_key')
client = OpenAI(base_url="https://internlm-chat.intern-ai.org.cn/puyu/api/v1/",api_key=api_key)

content = """
书生浦语InternLM2.5是上海人工智能实验室于2024年7月推出的新一代大语言模型,提供1.8B、7B和20B三种参数版本,以适应不同需求。
该模型在复杂场景下的推理能力得到全面增强,支持1M超长上下文,能自主进行互联网搜索并整合信息。
"""
prompt = f"""
请帮我从以下``内的这段模型介绍文字中提取关于该模型的信息,要求包含模型名字、开发机构、提供参数版本、上下文长度四个内容,以json格式返回。
`{content}`
"""
res = internlm_gen(prompt,client)
res_json = json.loads(res)
print(res_json)
image
image

根据报错信息初步判断,API 调用返回的 response 可能不符合预期,res 可能是空字符串或没有包含有效 JSON 数据。因此在res输出这一句打上断点查看返回的内容:

image
image

可以观察到,大模型返回的res中以````json`和`````作为开头和结尾的标记。因此直接把这一部分去掉就好。

通常来说,这些标记头能够帮助分离代码或特定格式的数据,以便更加清晰和直观地展示内容。

res = internlm_gen(prompt,client)
# res_json = json.loads(res)
# print(res_json)

try:
    # 移除代码块标记
    cleaned_res = res.strip('```json').strip('```').strip()
    # 解析为 JSON
    res_json = json.loads(cleaned_res)
    print(res_json)
except json.JSONDecodeError as e:
    print(f"JSON 解析失败: {e}")
    print(f"清理后的返回内容: {cleaned_res}")

最终得到的输出如下:

image
image

这样一个程序上的小问题就被成功解决了。