从加载到对话:使用 Llama-cpp-python 本地运行量化 LLM 大模型(GGUF)

(无需显卡)使用 Llama-cpp-python 在本地加载具有 70 亿参数的 LLM 大语言模型,通过这篇文章你将学会用代码创建属于自己的 GPT。
建议阅读完 19a 的「前言」和「模型下载」部分后再进行本文的阅读。

代码文件下载:Llama-cpp-python | 🎡 AI Chat 脚本

文章目录

  • Llama-cpp-python
    • 环境配置
    • GGUF
      • 安装库
      • 导入库
      • 本地导入模型
      • 自动下载并导入模型
      • 推理测试
      • 卸载到 GPU 加速推理
    • 流式输出
    • 多轮对话
    • 用 🎡 脚本感受 AI 对话(可选)
      • 克隆仓库
      • 执行脚本
      • 加载和保存历史对话
    • 参考链接

Llama-cpp-python

环境配置

为了确保后续的 “offload”(卸载到 GPU)功能正常工作,需要进行一些额外的配置。

首先,找到 CUDA 的安装路径(你需要确保已经安装了 CUDA):

find /usr/local -name "cuda" -exec readlink -f {} \;

参数解释

  • -name "cuda":在 /usr/local 目录下搜索名为 “cuda” 的文件或目录。
  • -exec readlink -f {} \;:对找到的每个文件或目录执行 readlink -f,获取其完整的绝对路径。

假设输出如下:

/usr/local/cuda-12.1
...

复制这个路径,设置 CUDA_HOME 环境变量:

export CUDA_HOME=/usr/local/cuda-12.1

接下来,安装 llama-cpp-python

CMAKE_ARGS="-DGGML_CUDA=on \
            -DCUDA_PATH=${CUDA_HOME} \
            -DCUDAToolkit_ROOT=${CUDA_HOME} \
            -DCUDAToolkit_INCLUDE_DIR=${CUDA_HOME} \
            -DCUDAToolkit_LIBRARY_DIR=${CUDA_HOME}/lib64 \
            -DCMAKE_CUDA_COMPILER=/usr/local/cuda/bin/nvcc" \
FORCE_CMAKE=1 \
pip install --upgrade --force-reinstall llama-cpp-python --no-cache-dir --verbose

[!note]

如果仅在 CPU 上运行,可以直接使用 pip install llama-cpp-python 进行安装。

否则,请确保系统已安装 CUDA,可以通过 nvcc --version 检查。

GGUF

以 bartowski/Mistral-7B-Instruct-v0.3-GGUF 为例进行演示。你将在模型界面查看到以下信息:

image-20241007093704658

可以看到 4-bit 量化有 IQ4_XSQ4_K_SIQ4_NLQ4_K_M 四种,出于性能的考虑,我们将选择 Q4_K_M 进行加载。

文件名量化类型文件大小描述
Mistral-7B-Instruct-v0.3-Q4_K_M.ggufQ4_K_M4.37GB质量较好,权重每位约占 4.83 比特,推荐使用
Mistral-7B-Instruct-v0.3-Q4_K_S.ggufQ4_K_S4.14GB略低于 Q4_K_M 的质量,但节省更多空间,推荐使用
Mistral-7B-Instruct-v0.3-IQ4_NL.ggufIQ4_NL4.13GB质量不错,体积略小于 Q4_K_S,性能相近,推荐使用
Mistral-7B-Instruct-v0.3-IQ4_XS.ggufIQ4_XS3.91GB质量不错,体积小于 Q4_K_S,性能相近,推荐使用

[!NOTE]

Q:这些量化类型到底是什么?
A:拓展阅读:《d. 如何加载 GGUF 模型(分片/Shared/Split/00001-of-0000…的解决方法)》,其中还会以 Qwen2.5-7B 为例讲述分片模型的加载方式。

安装库

pip install gguf

导入库

from llama_cpp import Llama

下面介绍两种导入模型的方法,实际执行时在本地导入和自动下载中选择一种即可。

本地导入模型

根据模型路径导入模型,注意,文件位于 <model_name> 文件夹下,以当前下载的文件为例:

# 指定本地模型的路径
model_path = "./Mistral-7B-Instruct-v0.3-GGUF/Mistral-7B-Instruct-v0.3-Q4_K_M.gguf"

# 加载模型
llm = Llama(
    model_path=model_path,
    #n_gpu_layers=-1,  # 取消注释使用 GPU 加速
    #verbose=False,  # 取消注释禁用详细日志输出
)

自动下载并导入模型

对于 llama-cpp-python,入乡随俗使用 repo_id 变量名,但本质是和之前一致的,filename 可以使用通配符,比如 "*Q4_K_M.gguf"

# 指定仓库的名称和文件名
repo_id = "bartowski/Mistral-7B-Instruct-v0.3-GGUF"
filename = "Mistral-7B-Instruct-v0.3-Q4_K_M.gguf"
#filename = "*Q4_K_M.gguf"  # 使用通配符也是可以的

# 下载并加载模型
llm = Llama.from_pretrained(
    repo_id=repo_id,
    filename=filename,
    #n_gpu_layers=-1,  # 取消注释使用 GPU 加速
    #verbose=False,  # 取消注释禁用详细日志输出
)

[!tip]

二者的函数区别在于 Llama()Llama.from_pretrained()

推理测试

使用以下代码进行简单的推理测试:

# 输入文本
input_text = "Hello, World!"

# 生成输出
output = llm(input_text, max_tokens=50)

# 打印生成的文本
print(output['choices'][0]['text'])

输出

Llama.generate: 4 prefix-match hit, remaining 1 prompt tokens to eval
llama_perf_context_print:        load time =      28.32 ms
llama_perf_context_print: prompt eval time =       0.00 ms /     1 tokens (    0.00 ms per token,      inf tokens per second)
llama_perf_context_print:        eval time =       0.00 ms /   100 runs   (    0.00 ms per token,      inf tokens per second)
llama_perf_context_print:       total time =    1563.56 ms /   101 tokens


Welcome to the latest post on my blog. Today, we will discuss an interesting topic: “How to create a website using JavaScript, HTML, and CSS“. This article is for those who are new to web development or want to learn the basics of creating a website using these technologies. Let’s dive in!

# Prerequisites

Before we start, I would like to mention that I am assuming that you have some basic knowledge of HTML, CSS,

每次生成都会打印一些时间方面的信息,设置 Llama() 的参数 verbose 为 False 可以禁止这个行为。

卸载到 GPU 加速推理

当前的模型默认被部署在 CPU 上,如果你的电脑拥有显卡且大于 5G 显存,那么可以增加 n_gpu_layers 参数将部分计算卸载(offload)到 GPU,以加速推理。修改加载模型的代码如下:

# 本地加载并卸载到 GPU
llm = Llama(
    model_path=model_path,
    n_gpu_layers=-1  # 将所有层卸载到 GPU
    verbose=False,  # 禁用详细日志输出
)

# 或者,自动下载并卸载到 GPU
llm = Llama.from_pretrained(
    repo_id=repo_id,
    filename=filename,
    n_gpu_layers=-1  # 将所有层卸载到 GPU
    verbose=False,  # 禁用详细日志输出
)

如果你的显卡不足 5G,可以设置卸载的具体层数,例如 n_gpu_layers=20,根据你的显存大小调整该参数。

P.S. 不卸载层是允许的,使用 CPU 一样可以进行推理,简单参考下面的表格:

设备tokens/sms/tokens/100 tokens
CPU11.4887.088.71
GPU66.8514.961.50

注:tokens/s 为每秒生成的 Token 数量,ms/token 为生成每个 Token 所需的毫秒数,s/100 tokens 为生成 100 个 Token 所需的秒数。

流式输出

Llama-cpp-python 的流式输出只需要在 create_chat_completion() 中传递参数 stream=True 就可以开启,以本地模型导入为例:

prompt = "人工智能的未来发展方向是什么?"

output = llm.create_chat_completion(
    messages=[{
        "role": "user",
        "content": prompt
    }],
    max_tokens=200,
    stream=True
)

for chunk in output:
    delta = chunk['choices'][0]['delta']
    if 'role' in delta:
        print(delta['role'], end=': ', flush=True)
    elif 'content' in delta:
        print(delta['content'], end='', flush=True)

输出

流式输出 llama-cpp-python

代码解释

  • for chunk in output::遍历模型生成的每一个数据块(chunk)。
    • delta = chunk['choices'][0]['delta']

      • 每个chunk包含一个choices列表,这里只取第一个选择(choices[0])。
      • delta包含了当前数据块中的增量信息,可能是角色(role)信息或内容(content)信息。
    • if 'role' in delta:

      • 如果delta中包含'role'键,说明这是角色信息(例如 “assistant”)。
      • print(delta['role'], end=': '):打印角色名,并以冒号和空格结尾,例如“assistant: ”,这是自定义行为,当然也可以 pass 掉。
    • elif 'content' in delta:

      • 如果delta中包含'content'键,说明这是实际的回答内容。
        • print(delta['content'], end=''):打印内容,不换行,以便逐步显示生成的回答,注意,在这里参数 end='' 是正确打印所必须的。

[!note]

查看 output 的构造

from itertools import islice

prompt = "人工智能的未来发展方向是什么?"

output = llm.create_chat_completion(
    messages=[{
        "role": "user",
        "content": prompt
    }],
    max_tokens=200,
    stream=True
)

print(type(output))

# 将生成器转换为列表
output_list = list(itertools.islice(output, 3))

# 获取前 3 个条目
output_list[:3]

输出(只需要查看其中的 delta):

<class 'generator'>
[{'id': 'chatcmpl-848b2e9b-7d70-4a7b-99aa-74b8206721db',
'model': './Mistral-7B-Instruct-v0.3-GGUF/Mistral-7B-Instruct-v0.3-Q4_K_M.gguf',
'created': 1728562647,
'object': 'chat.completion.chunk',
'choices': [{'index': 0,
 'delta': {'role': 'assistant'},
 'logprobs': None,
 'finish_reason': None}]},
{'id': 'chatcmpl-848b2e9b-7d70-4a7b-99aa-74b8206721db',
'model': './Mistral-7B-Instruct-v0.3-GGUF/Mistral-7B-Instruct-v0.3-Q4_K_M.gguf',
'created': 1728562647,
'object': 'chat.completion.chunk',
'choices': [{'index': 0,
 'delta': {'content': ' '},
 'logprobs': None,
 'finish_reason': None}]},
{'id': 'chatcmpl-848b2e9b-7d70-4a7b-99aa-74b8206721db',
'model': './Mistral-7B-Instruct-v0.3-GGUF/Mistral-7B-Instruct-v0.3-Q4_K_M.gguf',
'created': 1728562647,
'object': 'chat.completion.chunk',
'choices': [{'index': 0,
 'delta': {'content': '人'},
 'logprobs': None,
 'finish_reason': None}]}]

将刚刚对于流式输出的处理抽象为函数便于后续调用:

def handle_stream_output(output):
    """
    处理流式输出,将生成的内容逐步打印出来。
    
    参数:
        output: 生成器对象,来自 create_chat_completion 的流式输出
    """
    for chunk in output:
        delta = chunk['choices'][0]['delta']
        if 'role' in delta:
            print(f"{delta['role']}: ", end='', flush=True)
        elif 'content' in delta:
            print(delta['content'], end='', flush=True)
            
# 使用示例
prompt = "人工智能的未来发展方向是什么?"

output = llm.create_chat_completion(
    messages=[{
        "role": "user",
        "content": prompt
    }],
    max_tokens=200,
    stream=True
)

handle_stream_output(output)

函数解释

  • handle_stream_output
    • 接收一个生成器对象 output,遍历每个数据块 chunk
    • 从每个 chunk 中提取 delta 信息。
    • 根据 delta 中的键值,分别处理 rolecontent 信息。
    • 使用 flush=True 确保内容实时打印。

多轮对话

让我们自定义一个交互的对话类(需要注意到 handle_stream_output() 有所修改)。

from llama_cpp import Llama

def handle_stream_output(output):
    """
    处理流式输出,将生成的内容逐步打印出来,并收集完整的回复。

    参数:
        output: 生成器对象,来自 create_chat_completion 的流式输出

    返回:
        response: 完整的回复文本
    """
    response = ""
    for chunk in output:
        delta = chunk['choices'][0]['delta']
        if 'role' in delta:
            print(f"{delta['role']}: ", end='', flush=True)
        elif 'content' in delta:
            content = delta['content']
            print(content, end='', flush=True)
            response += content
    return response

class ChatSession:
    def __init__(self, llm):
        self.llm = llm
        self.messages = []

    def add_message(self, role, content):
        """
        添加一条消息到会话中。

        参数:
            role: 消息角色,通常为 'user' 或 'assistant'
            content: 消息内容
        """
        self.messages.append({"role": role, "content": content})

    def get_response_stream(self, user_input):
        """
        获取模型对用户输入的响应(流式输出)。

        参数:
            user_input: 用户输入的文本

        返回:
            response: 完整的回复文本
        """
        self.add_message("user", user_input)
        
        try:
            output = self.llm.create_chat_completion(
                messages=self.messages,
                stream=True  # 开启流式输出
            )
            
            response = handle_stream_output(output)  # 同时打印和收集回复
            
            self.add_message("assistant", response.strip())
            return response.strip()
        except Exception as e:
            print(f"\n发生错误: {e}")
            return ""

# 初始化模型(假设使用本地路径)
model_path = "./Mistral-7B-Instruct-v0.3-GGUF/Mistral-7B-Instruct-v0.3-Q4_K_M.gguf"
llm = Llama(
    model_path=model_path,
    n_gpu_layers=-1,  # 根据需要卸载到 GPU
    verbose=False,    # 禁用详细日志输出
)

# 创建会话实例
chat = ChatSession(llm)
        
# 开始对话
while True:
    prompt = input("User: ")
    # 退出对话条件(当然,你也可以直接终止代码块)
    if prompt.lower() in ["exit", "quit", "bye"]:
        print("Goodbye!")
        break
    chat.get_response_stream(prompt)
    print()  # 换行以便下一次输入,这是因为之前的 print 都设置了 end=''

输出

User:  如果你是大模型面试官,你会怎么出面试题
assistant:  以下是一些可能的大模型面试题:

1. 解释什么是深度学习和卷积神经网络,以及它们的应用场景。
2. 描述你对数据预处理和特征工程的了解,并提供一个实际使用例子。
3. 如何选择合适的模型、优化器和损失函数,以及如何评估模型性能?
4. 解释你对TensorFlow和PyTorch的了解,并提供一个使用它们的实际例子。
5. 如何处理不平衡数据集,以及你对样本平衡和数据增强方法的了解。
6. 如何使用Transfer Learning来提高模型性能,并提供一个实际例子。
7. 如何使用文本生成模型(如Seq2Seq模型)来进行机器翻译,文本摘要和情感分析?
8. 如何使用对象检测模型(如Faster R-CNN和YOLO)来进行目标检测?
9. 如何使用自编码器来进行数据压缩和特征学习?
10. 如何使用喂给网络(Feeding Networks)和Generative Adversarial Networks(GANs)来生成图像和文本?
11. 如何使用时序数据模型(如ARIMA和LSTM)来进行预测?
12. 如何使用回归树和随机森林来进行预测和分类?
13. 描述你对超参数调优的了解,包括网络架构、学习率和批大小等方面。
14. 如何使用K-means和朴素贝叶斯等聚类和分类方法?
15. 描述你对凸优化和
User:  对于第十个问题能否给我答案

发生错误: Requested tokens (530) exceed context window of 512

可以看到报错超过了上下文窗口的长度,让我们增加它:

llm = Llama(
    model_path=model_path,
    n_gpu_layers=-1,  # 根据需要卸载到 GPU
    n_ctx=4096,       # 设置上下文窗口大小
    verbose=False,    # 禁用详细日志输出
)

此时模型输出正常:

User:  如果你是大模型面试官,你会怎么出面试题
assistant:  以下是一些可能的大模型面试题:

1. 解释什么是深度学习和卷积神经网络,以及它们的应用场景。
2. 描述你对数据预处理和特征工程的了解,并提供一个实际使用例子。
3. 如何选择合适的模型、优化器和损失函数,以及如何评估模型性能?
4. 解释你对TensorFlow和PyTorch的了解,并提供一个使用它们的实际例子。
5. 如何处理不平衡数据集,以及你对样本平衡和数据增强方法的了解。
6. 如何使用Transfer Learning来提高模型性能,并提供一个实际例子。
7. 如何使用文本生成模型(如Seq2Seq模型)来进行机器翻译,文本摘要和情感分析?
8. 如何使用对象检测模型(如Faster R-CNN和YOLO)来进行目标检测?
9. 如何使用自编码器来进行数据压缩和特征学习?
10. 如何使用喂给网络(Feeding Networks)和Generative Adversarial Networks(GANs)来生成图像和文本?
11. 如何使用时序数据模型(如ARIMA和LSTM)来进行预测?
12. 如何使用回归树和随机森林来进行预测和分类?
13. 描述你对超参数调优的了解,包括网络架构、学习率和批大小等方面。
14. 如何使用K-means和朴素贝叶斯等聚类和分类方法?
15. 描述你对凸优化和随机 Forests等算法的了解。
User:  对于第十个问题能否给我答案
assistant:  给定一个生成图像和文本的问题,一种方法是使用Generative Adversarial Networks(GANs)。

GANs是一种深度学习模型,由两个子网络组成:生成器和判别器。生成器生成一组随机噪声并根据该噪声生成新的数据,而判别器试图区分生成的数据和真实数据。这两个子网络通过最小化一个对抗性损失函数来互相学习。

在生成图像方面,常用的GAN模型包括DCGAN(Deep Convolutional GAN)、CGAN(Conditional GAN)和WGAN(Wasserstein GAN)等。DCGAN使用 convolutional neural network 作为生成器,对于生成图像来说,DCGAN可以生成高质量的图像,但是它可能会生成一些不太可靠的图像,因为它是一种无条件生成器。

CGAN是DCGAN的一种扩展,它引入了条件信息,允许生成器根据特定条件(如类别标签)生成图像。WGAN是DCGAN的一种改进版本,它使用 Wasserstein 距离来替换了原来的对抗性损失函数,从而使得模型更加稳定。

在生成文本方面,常用的GAN模型包括SeqGAN(Sequence Generative Adversarial Nets)和StackGAN(Stack Generative Adversarial Networks)。SeqGAN通过使用RNNs(Recurrent Neural Networks)生成一系列单词来生成文本。StackGAN使用多个堆叠的GAN子网络来生成复杂的文本。

总之,GANs是一种强大的生成模型,可以生成高质量的图像和文本,但是它们也有一些问题,例如生成的数据可能存在模式缺陷,并且训练过程可能会收敛很慢。

至此,篇章告一段落 : )。

请注意,这只是一个简短的章节,其中还有许多知识尚未涉及,比如 Transformers 中的 Pipeline,对 Tokenizer 的更深入了解,以及 RAG 的应用等。由于内容过多,文章跳过了一些细节,预计闲暇时增设章节单独进行讲解。

用 🎡 脚本感受 AI 对话(可选)

这是可选的行为,脚本的代码处理逻辑与文章对应。

克隆仓库

# 如果已经克隆仓库的话跳过这行
git clone https://github.com/Hoper-J/AI-Guide-and-Demos-zh_CN

执行脚本

  1. 切换到 CodePlayground 文件夹:

    cd AI-Guide-and-Demos-zh_CN/CodePlayground
    
  2. 开始对话:

    # 使用方法:python chat.py <model_path> [可选参数],替换为你想用的 <model_path>
    # 本地加载
    python chat.py ../Demos/Mistral-7B-Instruct-v0.3-GGUF/Mistral-7B-Instruct-v0.3-Q4_K_M.gguf
    
    # 远程加载
    python chat.py bartowski/Mistral-7B-Instruct-v0.3-GGUF/Mistral-7B-Instruct-v0.3-Q4_K_M.gguf --remote
    
    # 远程加载(通配符版)
    python chat.py 'bartowski/Mistral-7B-Instruct-v0.3-GGUF/*Q4_K_M.gguf' --remote
    

加载和保存历史对话

使用 -i 参数指定文件加载,-o 参数进行保存,或者 -io 参数指明加载和保存的文件路径相同:

python chat.py <model_path> -io history

注意,Ctrl + C 将直接终止对话,只有使用 ‘exit’、‘quit’ 或 ‘bye’ 结束对话,或者使用 Ctrl + D (EOF) 退出时才会保存对话。

暂时仅支持与拥有 tokenizer.chat_template 属性的模型对话。

参考链接

  • llama-cpp-python - Docs
  • Example with stream = True? #319

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

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

相关文章

Vue3 + TypeScript + Vite + Echarts

Vue3 TypeScript Vite Echarts 1、创建工程 npm create vitelatestcd echarts npm install npm run dev2、安装项目依赖模块 npm install types/node --save-devnpm install vue-router4npm install animate.css --save npm install gsap --savenpm install fetch --save …

跨境电商干货:Etsy选品及相关运营技巧分享

Etsy作为一个吸引了全球将近一亿消费者的电子商务平台&#xff0c;因其聚焦小众、原创、设计产品的特点而拥有相当不错的流量和潜力&#xff0c;如果需要优化自己的Etsy店铺选品工作&#xff0c;可以参考以下技巧。 一、选品方向 1.按需求 Etsy主张售卖富有创意的、由卖家制作…

三电平逆变器:技术原理与实际应用

三电平逆变器&#xff1a;技术原理与实际应用&#xff08;网盘https://pan.baidu.com/s/1KRV4DBMChwZiu5lKgo0bEA 提取码 8v8p&#xff09; 中点钳位三电平逆变器的特性 优点 1、在换流过程中&#xff0c;每个功率半导体器件所承受的电压均为Ud/2。这有助于逆变器电压等级和…

VScode中CMake无高亮(就是没有补全的提示)

在我学的过程中我发现我的CMake是这样的&#xff0c;如下图 但在教学视频里是这样的&#xff08;如下图&#xff09; 这非常的难受&#xff0c;所以疯狂的找&#xff0c;最后是CMake报错有 原因就是&#xff1a;本地没有配置环境变量&#xff0c;解决方法是下一个cmake然后直接…

【C】分支与循环2--while/for/do-while/goto以及break和continue在不同循环中的辨析~

分支与循环 while循环 if与while的对比 if(表达式)语句&#xff1b;while(表达式)语句&#xff1b;下面来看一个例子&#xff1a; 用 if 写&#xff1a; #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int main() {if (1)printf("hehe");//if后面条…

【千库网-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

3、Docker搭建MQTT及Spring Boot 3.x集成MQTT

一、前言 本篇主要是围绕着两个点&#xff0c;1、Docker 搭建单机版本 MQTT&#xff08;EMQX&#xff09;&#xff0c;2、Spring Boot 3.x 集成 MQTT&#xff08;EMQX&#xff09;&#xff1b; 而且这里的 MQTT&#xff08;EMQX&#xff09;的搭建也只是一个简单的过程&#x…

uibot发送邮件:自动化邮件发送教程详解!

uibot发送邮件的操作指南&#xff1f;uibot发送邮件的两种方式&#xff1f; 在现代办公环境中&#xff0c;自动化流程的引入极大地提高了工作效率。uibot发送邮件功能成为了许多企业和个人实现邮件自动化发送的首选工具。AokSend将详细介绍如何使用uibot发送邮件。 uibot发送…

RHCE的学习(1)

一、 Linux的例行性工作 场景&#xff1a; 生活中&#xff0c;我们有太多场景需要使用到闹钟&#xff0c;比如早上 7 点起床&#xff0c;下午 4 点开会&#xff0c;晚上 8 点购物&#xff0c;等等。 在 Linux 系统里&#xff0c;我们同样也有类似的需求。比如我们想在凌晨 1 …

C++进阶:map和set的使用

目录 一.序列式容器和关联式容器 二.set系列的使用 2.1set容器的介绍 2.2set的构造和迭代器 2.3set的增删查 2.4insert和迭代器遍历的样例 2.5find和erase的样例 ​编辑 2.6multiset和set的差异 2.7简单用set解决两道题 两个数组的交集 环形链表二 三.map系列的使用…

Android Framework AMS(04)startActivity分析-1(am启动到ActivityThread启动)

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节主要解读AMS通过startActivity启动Activity的整个流程的第一阶段&#xff1a;从am启动到ActivityThread启动。 第二阶段文章链接为&#xf…

FFmpeg的简单使用【Windows】--- 视频倒叙播放

实现功能 点击【选择文件】按钮可以选择视频&#xff0c;当点击【开始处理】按钮之后&#xff0c;会先将视频上传到服务器&#xff0c;然后开始进行视频倒叙播放的处理&#xff0c;当视频处理完毕之后会将输出的文件路径返回&#xff0c;同时在页面中将处理好的视频展示出来。…

SHELL脚本之重定向符号的使用。

一.shell脚本&#xff08;对应完成某一个功能的命令熟悉与否&#xff0c;决定着shell脚本的熟练与否。&#xff09; 一个shell脚本就是一个普通的文本文件。 作用&#xff1a;将重复执行的操作写成脚本&#xff0c;自动执行。 二.Linux操作系统中重定向符号的使用。 类型&a…

ESP32接入扣子(Coze) API使用自定义智能体

使用ESP32接入Coze API实现聊天机器人的教程 本示例将使用ESP32开发板通过WiFi接入 Coze API&#xff0c;实现一个简单的聊天机器人功能。用户可以通过串口向机器人输入问题&#xff0c;ESP32将通过Coze API与智能体进行通信&#xff0c;并返回对应的回复。本文将详细介绍了如…

selenium有多个frame页时的操作方法(5)

之前文章我们提到&#xff0c;在webdriver.WebDriver类有一个switch_to方法&#xff0c;通过switch_to.frame()可以切换到不同的frame页然后才再定位某个元素做一些输入/点击等操作。 比如下面这个测试网站有2个frame页&#xff1a;http://www.sahitest.com/demo/framesTest.h…

wordpress 子比主题美化 四宫格 多宫格 布局插件

wordpress 主题美化 四宫格 多宫格 布局插件&#xff08;只在子比主题上测试过&#xff0c;其它主题没测试&#xff09; A5资源网四宫格布局插件是一个功能丰富的WordPress插件,专为创建自适应的四宫格布局而设计。这个插件具有以下主要特点: 灵活的布局: 支持1到8个宫格的自定…

如何设置 GitLab 密码长度?

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 60天专业…

星海智算:【RVC】无需部署一键启动

镜像介绍 RVC全称 Retrieval-based-Voice-Conversion-WebUI 简称 RVC 一个基于VITS的简单易用的语音转换&#xff08;变声器&#xff09;框架 RVC 是一款前沿的音色替换项目&#xff0c;可以进行歌曲的翻唱&#xff0c;和实时的变声&#xff0c;具有低延迟、优秀的变声效果、…

【可答疑】基于51单片机的自动烘手器(含仿真、代码、报告、演示视频等)

✨哈喽大家好&#xff0c;这里是每天一杯冰美式oh&#xff0c;985电子本硕&#xff0c;大厂嵌入式在职0.3年&#xff0c;业余时间做做单片机小项目&#xff0c;有需要也可以提供就业指导&#xff08;免费&#xff09;~ &#x1f431;‍&#x1f409;这是51单片机毕业设计100篇…

C++ 算法学习——1.8 快速幂算法

背景知识&#xff1a; 1.位运算 在C中&#xff0c;位运算是对整数类型的位进行操作的一种运算方式。常见的位运算符包括按位与&#xff08;&&#xff09;、按位或&#xff08;|&#xff09;、按位异或&#xff08;^&#xff09;、取反&#xff08;~&#xff09;、左移&am…