llama.cpp的C语言API使用

我们知道,一般运行大语言模型都是在Python上运行的,可是Python的性能太差了,不适合用于生产环境,因此可以采用llama.cpp提供的API在C语言上运行大模型。

llama.cpp的下载

Windows下的下载

我们需要下载llama.cpp的两个部分,分别是它的源代码和windows预编译包。它的源代码直接在github上下载即可:

GitHub - ggerganov/llama.cpp: LLM inference in C/C++

它的预编译包在这里下载:
Releases · ggerganov/llama.cpp

Linux下的下载

linux下只需要下载源代码,然后编译即可:

make

如果想下载GPU加速的,则输入:

make GGML_CUDA=1

gguf文件的获取

从huggingface中下载

在llama.cpp上运行大语言模型需要一个gguf格式的文件,存储模型的相关信息。gguf文件可以从huggingface上直接获取,如:bartowski/Llama-3.2-1B-Instruct-GGUF · HF Mirror,然后选择一个合适的镜像即可。

从transformers中转换

当然,也可以从transformers模型中转换。在llama.cpp的源代码包下输入如下命令:

pip install -r requirements.txt
python convert_hf_to_gguf.py  <transfomers模型路径> --outtype f16 --outfile  <格式转换后的模型路径.gguf>

在其中,transformers模型路径是一个目录,目录里包括模型信息和分词器信息,–outtype指定的是量化信息,用于减小推理时的显存资源消耗,可以选择f32,f16,q8_0,q4_k_m等。–outfile是转换后的gguf路径,是一个.gguf格式的文件

API接口的使用

在使用API接口前,我们需要先创建一个文件夹,作为项目文件夹。然后把源代码包中的include/llama.hggml/src下的所有头文件全部复制到这个项目文件夹中,接着把预编译包中的所有dll文件复制进去(Linux下复制函数库),然后创建main.c,编写main函数。

在使用API完成推理的过程中,需要依次经历以下几步:

  1. 加载模型

  2. 创建上下文

  3. 获得词汇表

  4. 处理提示词

  5. 创建批次

  6. 设置采样器

  7. 循环进行解码和采样

  8. 释放资源

接下来对每一步用到的函数进行讲解:

加载模型

在加载模型时,需要先设定参数,在加载模型。通常获取默认参数即可。

获取默认参数的函数是llama_model_default_params,其原型如下:

struct llama_model_params llama_model_default_params(void);

它需要一个llama_model_params结构体来接它的返回值,有了这个返回值,就可以调用llama_model_load_from_file函数,用于加载模型,这个函数的原型如下:

struct llama_model * llama_model_load_from_file(
                             const char * path_model,
              struct llama_model_params   params);

它返回一个llama_model结构体的指针,就是从路径中获取到的模型的指针,path_model表示gguf文件的路径,params是加载模型时的参数。

创建上下文

创建上下文时同样需要参数,获取其默认参数的函数是llama_context_default_params,其原型如下:

struct llama_context_params llama_context_default_params(void);

需要一个llama_context_params的结构体来接它的返回值,有了这个返回值,就可以调用llama_init_from_model函数,用于创建上下文,这个函数的原型如下:

struct llama_context * llama_init_from_model(
                     struct llama_model * model,
            struct llama_context_params   params);

它的第一个参数就是加载后的模型。第二个参数就是刚创建的参数,返回一个llama_context结构体的指针,表示初始化的上下文。

获得词汇表

获得词汇表采用llama_model_get_vocab函数,其原型如下:

const struct llama_vocab *llama_model_get_vocab(const struct llama_model* model);

它接受一个模型,返回这个模型中的词汇表,存储到llama_vocab结构体中,并返回地址。

处理提示词

在将提示词传入模型前,需要对其进行标记化(tokenize,又叫序列化),将文字转换为一个数组,这样才可以让模型理解这段文字。

处理提示词的关键函数是llama_tokenize,它用于标记化一段文字,的原型如下:

int32_t llama_tokenize(
        const struct llama_vocab * vocab,
                      const char * text,
                         int32_t   text_len,
                     llama_token * tokens,
                         int32_t   n_tokens_max,
                            bool   add_special,
                            bool   parse_special);

vocab是词汇表,text是文字,text_len是文字长度,tokens指向的地址是标记化之后的存储位置,n_tokens_max是序列化地址的最大容纳长度,add_special是是否增加特殊标记,即段首标识和段末标识,parse_special表示是否解析特殊表示,包括段首标识、段末标识等。

如果成功,这个函数将返回标记化的数量,即tokens的有效长度;如果失败,这个函数将返回负数。

提示词不仅包括语言本身,还包括一些特殊标识,如llama的提示词样例如下:

<|begin_of_text|><|start_header_id|>system<|end_header_id|>

Cutting Knowledge Date: December 2023
Today Date: 26 Jul 2024

{system_prompt}<|eot_id|><|start_header_id|>user<|end_header_id|>

{prompt}<|eot_id|><|start_header_id|>assistant<|end_header_id|>

在创建提示词的时候,需要注意包括这些特使标识。

创建批次

模型推理前,需要为推理创建一个批次。

创建批次采用llama_batch_get_one函数,这个函数的原型如下:

struct llama_batch llama_batch_get_one(
                  llama_token * tokens,
                      int32_t   n_tokens);

它会返回一个llama_batch结构体,表示创建的批次。参数tokens表示标记化后的数据,可以是一个数组,n_tokens是这个数组的有效长度。这两个参数都可以从llama_tokenize中获得。

设置采样器

采样器用于指定采样的方式,决定了以什么样的方式确定候选词。为了让采样方式多样化,同时进行多种采样,可以采取采样器链。如下代码就定义了一个采样器链:

struct llama_sampler_chain_params sparams = llama_sampler_chain_default_params();
struct llama_sampler *sampler = llama_sampler_chain_init(sparams);
llama_sampler_chain_add(sampler, llama_sampler_init_temp(0.8));
llama_sampler_chain_add(sampler, llama_sampler_init_top_k(50));
llama_sampler_chain_add(sampler, llama_sampler_init_top_p(0.9, 1));
long seed = time(NULL);
llama_sampler_chain_add(sampler, llama_sampler_init_dist(seed));

采样器链中,可以增加如下类型的采样器:

基础采样器

  • 贪婪采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_greedy(void);
    

    每次选择当前概率最高的词元作为输出,不考虑随机性。

  • 随机采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_dist(uint32_t seed);
    

    基于随机分布进行采样,seed 用于初始化随机数生成器。

概率调整采样器

  • Softmax 采样器

    DEPRECATED(LLAMA_API struct llama_sampler * llama_sampler_init_softmax(void));
    

    按照词元的 logits 对候选词元进行降序排序,并计算基于 logits 的概率。注意:不推荐在完整词汇表上使用,因为排序操作可能很慢,建议先进行 top-k 或 top-p 采样。

基于截断的采样器

  • Top-K 采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_top_k(int32_t k);
    

    选择概率最高的前 K 个词元进行采样,k 是截断的词元数量。

  • Nucleus 采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_top_p(float p, size_t min_keep);
    

    选择累积概率达到阈值 p 的最小词元集合进行采样,min_keep 是保留的最小词元数量。

  • Min-P 采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_min_p(float p, size_t min_keep);
    

    选择概率至少为 p 的词元进行采样,min_keep 是保留的最小词元数量。

  • 局部典型采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_typical(float p, size_t min_keep);
    

    选择与模型条件熵接近的词元进行采样,p 是截断阈值,min_keep 是保留的最小词元数量。

温度调整采样器

  • 温度采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_temp(float t);
    

    对 logits 进行缩放,公式为 li′​=li​/t。当 t <= 0.0f 时,保留最大 logit 的值,其余设置为负无穷。

  • 动态温度采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_temp_ext(float t, float delta, float exponent);
    

    动态调整温度,t 是基础温度,deltaexponent 是动态调整参数。

特殊采样器

  • XTC 采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_xtc(float p, float t, size_t min_keep, uint32_t seed);
    

    排除最可能的词元以增加创造性,p 是截断阈值,t 是温度,min_keep 是保留的最小词元数量,seed 是随机种子。

  • Mirostat 1.0 采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_mirostat(
        int32_t n_vocab, uint32_t seed, float tau, float eta, int32_t m, float mu);
    

    控制生成文本的交叉熵(surprise),n_vocab 是词汇表大小,tau 是目标交叉熵,eta 是学习率,m 是用于估计 s_hat 的词元数量,mu 是最大交叉熵。

  • Mirostat 2.0 采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_mirostat_v2(uint32_t seed, float tau, float eta, float mu);
    

    Mirostat 2.0 算法,参数与 Mirostat 1.0 类似,但实现更通用。

其他采样器

  • 语法采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_grammar(
        const struct llama_vocab * vocab, const char * grammar_str, const char * grammar_root);
    

    根据语法规则进行采样,vocab 是词汇表,grammar_str 是语法字符串,grammar_root 是语法根节点。

  • 惩罚采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_penalties(
        int32_t penalty_last_n, float penalty_repeat, float penalty_freq, float penalty_present);
    

    对重复词元进行惩罚,penalty_last_n 是考虑的最近 n 个词元,penalty_repeat 是重复惩罚,penalty_freq 是频率惩罚,penalty_present 是存在惩罚。

  • DRY 采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_dry(
        const struct llama_vocab * vocab, int32_t n_ctx_train, float dry_multiplier, float dry_base,
        int32_t dry_allowed_length, int32_t dry_penalty_last_n, const char ** seq_breakers, size_t num_breakers);
    

    用于减少重复和增强多样性的采样器,参数用于控制重复惩罚和序列中断。

  • Logit 偏置采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_logit_bias(
        int32_t n_vocab, int32_t n_logit_bias, const llama_logit_bias * logit_bias);
    

    对特定词元的 logits 进行偏置调整,n_vocab 是词汇表大小,n_logit_bias 是偏置词元数量,logit_bias 是偏置数组。

  • 填空采样器

    LLAMA_API struct llama_sampler * llama_sampler_init_infill(const struct llama_vocab * vocab);
    

    用于填空任务的采样器,主要用于在文本中间填充内容。

在采样器链的最后,必须是贪婪采样器、随机采样器和Mirostat采样器中的任意一种。

循环进行解码采样

在这里面,需要用到llama_decode函数进行解码,llama_sampler_sample函数进行采样,llama_detokenize函数进行反标记化(即将模型的输出转换为自然语言),最后需要将批次更新,增加刚输出的标识。

llama_decode的原型如下:

int32_t llama_decode(
            struct llama_context * ctx,
              struct llama_batch   batch);

它接受上下文和批次作为参数,返回值如果为0则成功,非0则失败。在成功解码后,就可以调用llama_sampler_sample函数进行采样,其原型如下:

llama_token llama_sampler_sample(struct llama_sampler * smpl, struct llama_context * ctx, int32_t idx);

它将会进行采样。它会对第idx个元素进行采样,如果idx为-1,则会采样最后一个。smpl是定义的采样器或采样器链,ctx是上下文。

llama_detokenize是反序列化函数,它的原型如下:

int32_t llama_detokenize(
        const struct llama_vocab * vocab,
               const llama_token * tokens,
                         int32_t   n_tokens,
                            char * text,
                         int32_t   text_len_max,
                            bool   remove_special,
                            bool   unparse_special);

llama_tokenize相反,它将tokens内的序列化数据转换为text内的文本数据,返回的是反序列化的长度。如果出错,返回负数。

接下来需要更新批次数据,这里面的更新指清除批次数据,并写入当前采样的数据:

batch.token[0] = next_token;
batch.n_tokens = 1;

释放资源

上面申请的模型、上下文、采样器、批次等都需要释放,代码如下:

llama_sampler_free(sampler);
llama_batch_free(batch);
llama_free(context);
llama_model_free(model);

完整代码

从[bartowski/Llama-3.2-1B-Instruct-GGUF · HF Mirror](bartowski/Llama-3.2-1B-Instruct-GGUF · HF Mirror)下载一个GGUF模型,进行测试。

完整代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include "llama.h"

#define MAX_TOKEN 10000

int main(void){
    // 创建模型
    struct llama_model_params model_params = llama_model_default_params();
    struct llama_model *model = llama_model_load_from_file("./Llama-3.2-1B-Instruct-f16.gguf", model_params);
	printf("create model down\n");
    
    // 创建上下文
    struct llama_context_params context_params = llama_context_default_params();
    struct llama_context *context = llama_init_from_model(model, context_params);
	printf("create context down\n");
    
    // 获得词汇表
    const struct llama_vocab *vocab = llama_model_get_vocab(model);
	printf("create vocab down\n");
    
    // 定义提示词
    char *prompt = 
	"<|begin_of_text|><|start_header_id|>user<|end_header_id|>Who are you?<|eot_id|><|start_header_id|>assistant<|end_header_id|>";
    
    // 对提示词进行标记化(tokenize)
    llama_token *tokens = (llama_token *)malloc(sizeof(llama_token) * MAX_TOKEN);
    int len = llama_tokenize(vocab, prompt, strlen(prompt), tokens, MAX_TOKEN, false, true);
	if (len < 0){
		fprintf(stderr, "Error:tokenize error\n");
		return -1;
	}
    printf("tokenize prompt down\n");
	
    // 创建批次
	struct llama_batch batch = llama_batch_get_one(tokens, len);
	printf("create batch down\n");
    
    // 初始化采样器链
    struct llama_sampler_chain_params sparams = llama_sampler_chain_default_params();
    struct llama_sampler *sampler = llama_sampler_chain_init(sparams);
	llama_sampler_chain_add(sampler, llama_sampler_init_temp(0.8));
    llama_sampler_chain_add(sampler, llama_sampler_init_top_k(50));
    llama_sampler_chain_add(sampler, llama_sampler_init_top_p(0.9, 1));
    long seed = time(NULL);
    llama_sampler_chain_add(sampler, llama_sampler_init_dist(seed));
	printf("create sampler chain down\n");
	
	// 循环
    llama_token next_token = LLAMA_TOKEN_NULL;
    llama_token eos = llama_vocab_eos(vocab);
    while (next_token != eos) {
		// 解码
		if(llama_decode(context, batch)){
			fprintf(stderr, "Error: decode error\n");
			return -1;
		}
		
        // 采样
        next_token = llama_sampler_sample(sampler, context, -1);
        
        // 反标记化
        char deprompt[100] = {0};
        if(llama_detokenize(vocab, &next_token, 1, deprompt, sizeof(deprompt) / sizeof(deprompt[0]), false, false) < 0){
			fprintf(stderr, "Error: detokenize error\n");
            return -1;
        }
        printf("%s", deprompt);
        
        // 更新 batch 以包含新生成的 token
        batch.token[0] = next_token;
		batch.n_tokens = 1;
    }
    
    // 释放资源
    llama_sampler_free(sampler);
	llama_batch_free(batch);
    llama_free(context);
    llama_model_free(model);
	free(tokens);
    
    return 0;
}

输出结果:

I'm an artificial intelligence model known as Llama. Llama stands for "Large Language Model Meta AI."

可以看到,模型没有问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/964342.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

期末数据库课程设计基于Java+MySQL+JDBC+JavaSwing实现的图书进销管理系统源代码+数据库

期末数据库课程设计&#xff0c; 图书进销信息管理系统 直接将数据库文件导入就可以快速建表 效果图 系统登录弹窗 书库管理页 信息查询页 图书销售页 系统设置页 编码&#xff1a; GBK 开发环境 jdk12MySQL8.0 推荐用IDEA运行项目 补充 UI美化&#xff08;引用当前系统…

DeepSeek最新图像模型Janus-Pro论文阅读

目录 论文总结 摘要 1. 引言 2. 方法 2.1 架构 2.2 优化的训练策略 2.4 模型扩展 3. 实验 3.1 实施细节 3.2 评估设置 3.3 与最新技术的比较 3.4 定性结果 4. 结论 论文总结 Janus-Pro是DeepSeek最新开源的图像理解生成模型&#xff0c;Janus-Pro在多模态理解和文…

深入解析“legit”的地道用法——从俚语到正式表达:Sam Altman用来形容DeepSeek: legit invigorating(真的令人振奋)

深入解析“legit”的地道用法——从俚语到正式表达 一、引言 在社交媒体、科技圈甚至日常对话中&#xff0c;我们经常会看到或听到“legit”这个词。比如最近 Sam Altman 在 X&#xff08;原 Twitter&#xff09;上发的一条帖子中写道&#xff1a; we will obviously deliver …

物联网领域的MQTT协议,优势和应用场景

MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;作为轻量级发布/订阅协议&#xff0c;凭借其低带宽消耗、低功耗与高扩展性&#xff0c;已成为物联网通信的事实标准。其核心优势包括&#xff1a;基于TCP/IP的异步通信机制、支持QoS&#xff08;服务质量&…

【Hadoop】Hadoop的HDFS

这里写目录标题 HDFS概述HDFS产出背景及定义HDFS产生背景HDFS定义 HDFS优缺点HDFS优点HDFS缺点 HDFS组成架构HDFS文件块大小 HDFS的Shell操作常用命令实操准备工作上传下载HDFS直接操作 HDFS的API操作客户端环境准备HDFS的API案例实操HDFS文件上传HDFS文件下载HDFS文件更名和移…

二、面向对象

一、结构体类型 结构体类型是一种自定义类型&#xff0c;用于创建我们游戏或者实际业务中的自定义类型. 代码中变量有通用的&#xff0c;可以使用结构体&#xff0c;包裹起来。 1、成员变量 /// <summary> /// 英雄结构体 /// </summary> struct Hero {//成员p…

基于机器学习的布伦特原油价格的分析与研究

项目&#xff1a;基于机器学习的布伦特原油价格的分析与研究 摘 要 布伦特原油期货及现货市场所构成的布伦特原油定价体系&#xff0c;最多时竟涵盖了世界原油交易量的80%&#xff0c;即使在纽约原油价格日益重要的今天&#xff0c;全球仍有约65%的原油交易量&#xff0c;是以…

excel实用问题:提取文字当中的数字进行运算

0、前言&#xff1a; 这里汇总在使用excel工作过程中遇到的问题&#xff0c;excel使用wps版本&#xff0c;小规模数据我们自己提取数据可行&#xff0c;大规模数据就有些难受了&#xff0c;因此就产生了如下处理办法。 需求&#xff1a;需要把所有文字当中的数字提取出来&…

贝叶斯-概率

起点&#xff1a;玩猜硬币游戏中发现贝叶斯定理貌似有很强的预测功能&#xff0c;细看还真有那么回事&#xff0c;因此研究研究。当然&#xff0c;看起来学精后不止可用来猜硬币&#xff0c;也可猜其它玩艺。 贝叶斯统计的基础是贝叶斯定理&#xff0c;贝叶斯定理的基础是条件…

信息安全专业2025最新毕业设计选题汇总:课题精选

目录 前言 毕设选题 开题指导建议 更多精选选题 选题帮助 最后 前言 大家好,这里是海浪学长毕设专题! 大四是整个大学期间最忙碌的时光&#xff0c;一边要忙着准备考研、考公、考教资或者实习为毕业后面临的升学就业做准备,一边要为毕业设计耗费大量精力。学长给大家整理…

[LeetCode]day13 19.删除链表的倒数第n个结点

19. 删除链表的倒数第 N 个结点 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&a…

2024年终总结来了

忘记发CSDN的年度总结了&#xff0c;今天补上吧 说实话&#xff0c;今年过得不是特别好&#xff0c;感觉遇到了瓶颈&#xff0c;人生变得迷茫起来。不知道大家有没有同样的感受 刚毕业的时候人生充满了憧憬&#xff0c;慢慢的随着年龄变大后&#xff0c;就会觉得一事无成&…

Haproxy+keepalived高可用集群,haproxy宕机的解决方案

Haproxykeepalived高可用集群&#xff0c;允许keepalived宕机&#xff0c;允许后端真实服务器宕机&#xff0c;但是不允许haproxy宕机&#xff0c; 所以下面就是解决方案 keepalived配置高可用检测脚本 &#xff0c;master和backup都要添加 配置脚本 # vim /etc/keepalived…

树莓派pico入坑笔记,故障解决:请求 USB 设备描述符失败,故障码(43)

今天心血来潮&#xff0c;拿出吃灰的pico把玩一下&#xff0c;打开thonny&#xff0c;上电&#xff0c;然后...... 上电识别不到端口&#xff0c;windows报错&#xff0c;请求 USB 设备描述符失败&#xff0c;故障码&#xff08;43&#xff09; 一开始以为是坏了&#xff08;磕…

从Transformer到世界模型:AGI核心架构演进

文章目录 引言:架构革命推动AGI进化一、Transformer:重新定义序列建模1.1 注意力机制的革命性突破1.2 从NLP到跨模态演进1.3 规模扩展的黄金定律二、通向世界模型的关键跃迁2.1 从语言模型到认知架构2.2 世界模型的核心特征2.3 混合架构的突破三、构建世界模型的技术路径3.1 …

2025年01月25日Github流行趋势

项目名称&#xff1a;it-tools 项目地址url&#xff1a;https://github.com/CorentinTh/it-tools项目语言&#xff1a;Vue历史star数&#xff1a;25298今日star数&#xff1a;212项目维护者&#xff1a;CorentinTh, apps/renovate, cgoIT, sharevb, marvin-j97项目简介&#xf…

鸿蒙Harmony-双向数据绑定MVVM以及$$语法糖介绍

鸿蒙Harmony-双向数据绑定MVVM以及$$语法糖介绍 1.1 双向数据绑定概念 在鸿蒙&#xff08;HarmonyOS&#xff09;应用开发中&#xff0c;双向数据改变&#xff08;或双向数据绑定&#xff09;是一种让数据模型和UI组件之间保持同步的机制&#xff0c;当数据发生变化时&#x…

【DeepSeek背后的技术】系列二:大模型知识蒸馏(Knowledge Distillation)

目录 1 引言2 操作步骤和公式说明2.1 准备教师模型&#xff08;Teacher Model&#xff09;和学生模型&#xff08;Student Model&#xff09;2.2 生成软标签&#xff08;Soft Labels&#xff09;2.3 定义蒸馏损失函数2.4 训练学生模型2.5 调整超参数2.6 评估与部署 3 其他知识蒸…

【BUUCTF杂项题】后门查杀、webshell后门

前言&#xff1a;Webshell 本质上是一段可在 Web 服务器上执行的脚本代码&#xff0c;通常以文件形式存在于 Web 服务器的网站目录中。黑客通过利用 Web 应用程序的漏洞&#xff0c;如 SQL 注入、文件上传漏洞、命令执行漏洞等&#xff0c;将 Webshell 脚本上传到服务器&#x…

SPI(Serial Peripheral Interface)串行外围设备接口

SPI概述&#xff1a; SPI协议最初由Motorola公司&#xff08;现为NXP Semiconductors的一部分&#xff09;在20世纪80年代中期开发。最初是为了在其68000系列微控制器中实现高速、高效的串行通信。该协议旨在简化微控制器与外围设备之间的数据传输。 1980年代&#xff1a;SPI协…