开源大模型的小白实操
一、前言本篇博客是在[一堂课带你走进生成式人工智能的原理]的理论基础上进行的实操讲解主要讲述如何使用开源的语言模型来进行实操进而加深对于大模型的了解。二、开源大模型一非开源大模型vs开源大模型在进行实操前我们首先要了解什么叫做开源大模型以及什么叫做非开源大模型。1.非开源大模型 (Closed-Source / Proprietary LLMs)a.定义非开源模型通常由大型科技公司开发模型的核心技术包括权重、架构、训练数据和底层代码被视为商业机密。用户无法直接接触到模型本身只能通过API 接口或网页应用来使用。b.核心优势性能天花板 (SOTA)通常拥有最顶级的参数量和最庞大的算力支持在逻辑推理、复杂指令遵循和多模态能力上往往代表着行业最高水平。开箱即用用户无需关心服务器、显卡、环境配置等底层基建只需调用 API 或登录网页即可使用极大地降低了使用门槛。稳定性与服务支持由大厂提供强大的服务器集群支持系统稳定性高并提供企业级的 SLA服务等级协议和客户支持。c.主要局限隐私与数据安全使用时必须将数据发送到提供商的云端服务器对于涉及高度机密数据的企业如金融、医疗可能存在合规风险。“黑盒”属性与受限的控制权无法对模型进行深度的底层修改或微调通常只能使用官方提供的微调接口存在被厂商限制功能或突然下架的风险供应商锁定。d.代表模型非开源的大模型主要有Google 的Gemini系列、OpenAI 的GPT系列以及Anthropic 的Claude 3系列等等。2.开源大模型 (Open-Source LLMs)a.定义开源模型是指开发者将模型的权重Weights、网络架构有时甚至包括训练代码和数据集公开发布。任何人都可以下载这些模型并在自己的设备或服务器上运行。b.核心优势数据绝对隐私可以完全在本地服务器或私有云上离线部署数据无需出域彻底解决隐私和数据泄露的担忧。极高的定制化自由度开发者可以访问模型的内部进行深度的二次开发、全量微调Fine-tuning、知识注入或架构修改打造最适合垂直领域的专属模型。避免供应商锁定不受制于任何单一平台或公司的定价策略和 API 限制。c.主要局限技术门槛高需要专业的算法工程师团队来负责模型的部署、环境搭建、算力调度和持续优化。硬件成本前置运行和微调大型开源模型需要昂贵的 GPU 资源。综合能力略逊一筹尽管顶级开源模型正在快速缩小差距但在最极端的复杂任务上通常仍略微落后于最顶级的闭源模型。c.主要局限开源的大模型主要有Meta 的Llama 3系列、阿里巴巴的Qwen (通义千问)系列等等。ps开源模型与闭源巨头之间的技术鸿沟正在以惊人的速度缩小。正如 NVIDIA CEO 黄仁勋在 2026 年的演讲中所展示的见上图左侧虽然在 2023 到 2024 年间闭源的‘前沿模型’蓝色趋势线保持着绝对的领先优势但进入 2025 年后以DeepSeek V3.2、Qwen 和 Kimi K2为代表的开源模型绿色趋势线呈现出了更陡峭的进化曲线。到 2026 年初顶尖的开源模型在基础智能Model Intelligence上已经成功触及了行业的天花板。这意味着我们在本地部署的免费模型其核心能力已经足以媲美甚至在部分场景下替代昂贵的商业 API。三、如何使用开源模型一在哪里下载开源模型如果您想第一时间获取全球最前沿的开源模型如Llama 3 系列、Mistral等Hugging Facehttps://huggingface.co/是不二之选。它提供了极度标准化的模型加载范式是进行底层算法研究和架构探索的必经之路。但是对于国内开发者来说下载动辄上百 GB 的大模型往往受制于网络环境。魔塔社区https://modelscope.cn/完美解决了这个痛点它不仅提供了国内顶级的下载带宽还深度聚合了Qwen、GLM等优秀的国产开源大模型是国内进行大模型开发落地的首选平台。二如何下载开源模型因为在国内使用Hugging Face下载模型时网络要么不稳定要么彻底连不上因此主要采用魔塔社区所提供的modelscope来进行模型的下载。服务器电脑环境ubuntu22.04-py311-torch2.3.1-1.35.0 8核32GCPU首先第一步在命令行中下载安装modelscope命令如下pip install modelscope为什么截图中我的pip前面需要加 感叹号!首先pip不是 Python 代码pip是 Python 的包管理工具它本身是一个独立的可执行程序需要在操作系统的命令行终端里运行。而我是在Jupyter Notebook中输入的命令因此要加感叹号这样就相当于告诉系统“这一行不是 Python 代码请把它当成操作系统的命令行Terminal/Shell指令来执行”。之后许多需要在命令行执行的代码我都是这样处理的就不再解释。然后就需要先在魔塔社区中找到你要下载的模型这里我拿Qwen/Qwen2.5-7B-Instruct模型举例找到模型后就可以下载了在命令行中输入以下命令modelscope download --model Qwen/Qwen2.5-7B-Instruct截图中显示系统正在下载模型并给出了模型下载到服务器的什么位置Downloading Model from https://www.modelscope.cn to directory: /mnt/workspace/.cache/modelscope/models/Qwen/Qwen2.5-7B-Instruct当系统运行结束若下载成功会给出以下提示这样就完成了使用modelscope下载开源模型。三简单实操一下开源模型首先需要先下载安装transformerspip install -U transformers在深度学习和大语言模型LLM领域transformers是 Hugging Face 提供的核心生态库可以说是加载和运行开源大模型的“引擎”因此需要下载安装。然后在Jupyter Notebook中输入以下Python代码model_id /mnt/workspace/.cache/modelscope/models/Qwen/Qwen2.5-7B-Instruct from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer AutoTokenizer.from_pretrained(model_id) model AutoModelForCausalLM.from_pretrained(model_id)第一行代码是定义路径。将刚刚通过 ModelScope 下载到服务器的文件夹路径赋值给了变量。这样做的好处是后续加载时程序不需要去公网下载而是直接从服务器本地读取速度极快。下面一块代码作用主要是从我们刚刚下载的transformers调用AutoTokenizer和AutoModelForCausalLM这里简单介绍一下AutoTokenizer和AutoModelForCausalLM1.AutoTokenizer尽职尽责的“翻译官”大模型是一个纯粹的数学模型神经网络它根本看不懂“你好”或者“Hello”这样的汉字或字母它只认识数字。AutoTokenizer的工作就是充当人类语言和机器数字之间的桥梁。它的主要工作编码 Encode当你输入一句“你好千问”时Tokenizer 会查阅它的“字典”即你下载的词表文件把这句话切分成一个个的词元Token然后转换成一串数字 ID。比如它可能会把“你好”转换成108386把“千问”转换成104134。它的逆向工作解码 Decode当大模型在内部经过复杂的数学运算吐出一串数字[334, 1993, ...]时人类是看不懂的。Tokenizer 会再次查阅字典把这串数字反向翻译成人类能看懂的汉字文本输出给你。为什么用Auto不同的模型切分单词的规则完全不同。用了AutoTokenizer你就不需要关心 Qwen 用的是什么分词算法比如 BPE它会自动读取本地的tokenizer_config.json帮你处理好一切。2.AutoModelForCausalLM预测未来的“大脑”这个名字虽然长但拆开来看非常清晰AutoModel自动模型 For用于 CausalLM因果语言建模。什么是 Causal LM因果语言模型“因果”Causal在这里是一个专业的学术名词。简单来说它的核心逻辑就是“单向的文字接龙”。它只能看到前面的“因”上文去预测后面的“果”下一个词。当模型接收到 Tokenizer 传来的数字 ID 后它会在庞大的神经网络中进行几百亿次的矩阵乘法计算最后输出词表中每一个词作为“下一个词”的概率分布。它的核心作用它就是那个加载了 14GB 权重文件.safetensors的本体。它包含了模型所有的知识、逻辑和推理能力。当你调用它的.generate()方法时它就会根据你给的上文不断循环预测下一个字直到生成完整的回答。为什么用Auto因为开源界有无数种模型架构Qwen 属于 Qwen2 架构LLaMA 属于 Llama 架构。AutoModelForCausalLM会自动读取你本地的config.json发现这是一个 Qwen 模型然后自动帮你调出对应的网络结构代码把权重严丝合缝地填充进去。代码执行过后因为我的服务器并没有GPU所以模型会加载到服务器的内存中。还记得上一篇博客中提到大模型就是在做文字接龙游戏那么Qwen2.5-7B-Instruct每次接的时候有多少Token可以选择呢print(模型可以选择的Token的数量是,tokenizer.vocab_size)tokenizer_vocab_size是模型的Token总数相当于大模型的字典结果显示是151,643说明Qwen2.5-7B-Instruct模型每次接下一个字的时候都要从151643中选一个。让我们看看字典中第一个Token最后一个Token分别是什么token_id 151642 print(编号为, token_id,的Token是,tokenizer.decode(token_id))token_id 151642 print(编号为, token_id,的Token是,tokenizer.decode(token_id))这里我们先引入两个函数encode和decodetokenizer.encode把人类语言变成机器密码加密核心作用将我们输入的自然语言字符串String切割并转换成大模型唯一能看懂的一串数字索引Token IDs / List[int]。底层工作流清理与标准化处理掉多余的空格、特殊字符等。切词Tokenization根据词表规则比如千问用的 BPE 算法把句子切成一个个的词块Token。查表映射拿着这些切好的词块去那 151,643 个字的“大字典”里查阅找出它们对应的数字编号。tokenizer.decode把机器密码翻译回人类语言解密核心作用这是encode的逆过程。当大模型经过复杂的矩阵乘法最终推算出一串数字 ID 时你需要用decode把这串数字还原成你能看懂的文本。底层工作流反向查表拿着数字 ID 列表去字典里找到对应的词块Token。拼接组合把这些词块像拼图一样拼凑起来。它会聪明地处理掉原本用于切词的特殊空格标记让句子读起来自然流畅。了解两个函数的功能后我们再看结果可以发现字典中第一个Token是感叹号(!)最后一个Token是汉字“父”。这里我在实际运行代码时还发现一个有意思的地方一共151643个Token按理说编号151642应该是最后一个但是当我试着decode编号大于等于151643时发现居然还有字符经过查阅资料发现运行tokenizer.vocab_size时它返回的151,643仅仅是千问模型通过 BPE 算法训练出来的普通人类语言、代码和符号的数量而大模型的词汇表其实分为“基础词表”和“外挂特殊词表”两部分。print(基础词表大小 (vocab_size):, tokenizer.vocab_size) print(真实总词表大小 (包含特殊Token):, len(tokenizer))这些“外挂特殊词表”又有什么作用呢原来大模型在训练好基础语言能力后为了能进行多轮对话、区分角色开发者强行往词表最后面“外挂”了几个魔法控制符比如|im_start|和|im_end|。这些特殊控制符不包含在vocab_size的统计里。它们的编号紧接着基础词表的最大编号往下排。因此第一个特殊控制符的编号就恰好是151,643接着是151,644以此类推。至于为什么外挂这些控制符后面我还会再次说明这一点。当然我们也可以将文字字符编码为Token的IDtoken_str 你好 print(token_str, 转化为Token的ID是, tokenizer.encode(token_str))哎你好明明是两个字怎么只有一个Token是不是模型实际上的和我们的设想的不太一样。我们再聚几个例子为了验证我们编码的结果的正确我们反过来再解码一次看看一致不一致这也表示前面的编码确实没有什么问题可见实践出真知不能想当然。再举一个有趣的现象空格句号是一个token59133但是缓缓位置句号空格就是两个Token1773和220这两个我还特地解码看了一下发现居然就是单独的句号和空格但是59133解码就是空格句号连在一起。可见Token和字符不一定是传统的一个对应一个而是每一个Token对应不同的字符或字符组合等等。既然每一个Toke对应的文本是多样的那么我们可以看看一个Token对应最长的文本长什么样子这里我根据Token对应字符的数量进行排序打印了前100的情况# 创建一个空列表用来存储所有的元组(长度, Token_ID, 文本内容) token_data [] print(正在解析 15 万个 Token请稍等几秒钟...) # 1. 遍历并收集所有 Token 的信息 for token_id in range(tokenizer.vocab_size): text tokenizer.decode([token_id]) # 将长度放在元组的第一个位置方便后续直接按长度排序 token_data.append((len(text), token_id, text)) # 2. 按照长度进行降序排序 # reverseTrue 表示从大到小排 (默认是升序) token_data.sort(reverseTrue) # 3. 打印前 100 名 print(- * 60) print( Qwen2.5-7B 词表最长 Token Top 100 榜单) print(- * 60) for i in range(100): length, tid, text token_data[i] # 使用 repr() 打印确保所有的空格、换行符等不可见字符都能原形毕露 print(fTop {i1:3} | 长度: {length:3} | ID: {tid:6} | 内容: {repr(text)})第一名居然是一个长度128的空格字符串说实话还是挺惊奇的。而观察其他较长的你也会发现各种奇奇怪怪的字符串都有。看完长的我们来看看短一点的代码在上面的基础上改一下排序顺序就可以了# 创建一个空列表用来存储所有的元组(长度, Token_ID, 文本内容) token_data [] print(正在解析 15 万个 Token请稍等几秒钟...) # 1. 遍历并收集所有 Token 的信息 for token_id in range(tokenizer.vocab_size): text tokenizer.decode([token_id]) # 将长度放在元组的第一个位置方便后续直接按长度排序 token_data.append((len(text), token_id, text)) # 2. 按照长度进行降序排序 # reverseTrue 表示从大到小排 (默认是升序) token_data.sort(reverseFalse) # 3. 打印前 100 名 print(- * 60) print( Qwen2.5-7B 词表最长 Token Top 100 榜单) print(- * 60) for i in range(100): length, tid, text token_data[i] # 使用 repr() 打印确保所有的空格、换行符等不可见字符都能原形毕露 print(fTop {i1:3} | 长度: {length:3} | ID: {tid:6} | 内容: {repr(text)})看一下短的一下子就有规律了居然和Token的ID顺序基本上完全一致并且这些短的文本基本上都是一个对应一个文本字符看来Token字典在设计的时候也是有一定的规律。