书生·浦语2.0(InternLM2)大模型实战--Day04 XTuner微调 | 1.8B 多模态Agent

在这里插入图片描述

  • 视频地址: https://b23.tv/QUhT6ni
  • 课程文档:https://github.com/InternLM/Tutorial/blob/camp2/xtuner/readme.md
  • 作业文档:https://github.com/InternLM/Tutorial/blob/camp2/xtuner/homework.md

XTuner 微调个人小助手认知
在本节课中讲一步步带领大家体验如何利用 XTuner 完成个人小助手的微调!

为了能够让大家更加快速的上手并看到微调前后对比的效果,那我这里选用的就是上一期的课后作业:用 QLoRA 的方式来微调一个自己的小助手!我们可以通过下面两张图片来清楚的看到两者的对比。

在这里插入图片描述

1. 环境准备

在这里插入图片描述
完成准备工作后我们就可以正式开始我们的微调之旅啦!

2.快速上手

可以通过下面这张图来简单了解一下 XTuner 的运行原理。
在这里插入图片描述

  1. 环境安装:假如我们想要用 XTuner 这款简单易上手的微调工具包来对模型进行微调的话,那我们最最最先开始的第一步必然就是安装XTuner!安装基础的工具是一切的前提,只有安装了 XTuner 在我们本地后我们才能够去思考说具体怎么操作。

  2. 前期准备:那在完成了安装后,我们下一步就需要去明确我们自己的微调目标了。我们想要利用微调做一些什么事情呢,那我为了做到这个事情我有哪些硬件的资源和数据呢?假如我们有对于一件事情相关的数据集,并且我们还有足够的算力资源,那当然微调就是一件水到渠成的事情。就像 OpenAI 不就是如此吗?但是对于普通的开发者而言,在资源有限的情况下,我们可能就需要考虑怎么采集数据,用什么样的手段和方式来让模型有更好的效果。

  3. 启动微调:在确定了自己的微调目标后,我们就可以在 XTuner 的配置库中找到合适的配置文件并进行对应的修改。修改完成后即可一键启动训练!训练好的模型也可以仅仅通过在终端输入一行指令来完成转换和部署工作!

2.1 环境安装

首先我们需要先安装一个 XTuner 的源码到本地来方便后续的使用。

# 如果你是在 InternStudio 平台,则从本地 clone 一个已有 pytorch 的环境:
# pytorch    2.0.1   py3.10_cuda11.7_cudnn8.5.0_0

studio-conda xtuner0.1.17
# 如果你是在其他平台:
# conda create --name xtuner0.1.17 python=3.10 -y

# 激活环境
conda activate xtuner0.1.17
# 进入家目录 (~的意思是 “当前用户的home路径”)
cd ~
# 创建版本文件夹并进入,以跟随本教程
mkdir -p /root/xtuner0117 && cd /root/xtuner0117

# 拉取 0.1.17 的版本源码
git clone -b v0.1.17  https://github.com/InternLM/xtuner
# 无法访问github的用户请从 gitee 拉取:
# git clone -b v0.1.15 https://gitee.com/Internlm/xtuner

# 进入源码目录
cd /root/xtuner0117/xtuner

# 从源码安装 XTuner
pip install -e '.[all]'

在这里插入图片描述
假如速度太慢可以 Ctrl + C 退出后换成

pip install -e '.[all]' -i https://mirrors.aliyun.com/pypi/simple/

在这里插入图片描述
假如在这一过程中没有出现任何的报错的话,那也就意味着我们成功安装好支持 XTuner 所运行的环境啦。下面准备好我们需要的数据集、模型和配置文件!

2.2 前期准备

2.2.1 数据集准备

为了让模型能够让模型认清自己的身份弟位,知道在询问自己是谁的时候回复成我们想要的样子,我们就需要通过在微调数据集中大量掺杂这部分的数据。

首先我们先创建一个文件夹来存放我们这次训练所需要的所有文件。

# 前半部分是创建一个文件夹,后半部分是进入该文件夹。
mkdir -p /root/ft && cd /root/ft

# 在ft这个文件夹里再创建一个存放数据的data文件夹
mkdir -p /root/ft/data && cd /root/ft/data

之后我们可以在 data 目录下新建一个 generate_data.py 文件,将以下代码复制进去,然后运行该脚本即可生成数据集。
假如想要加大剂量让他能够完完全全认识到你的身份,那我们可以吧 n 的值调大一点。

import json

# 设置用户的名字
name = '不要姜葱蒜大佬'
# 设置需要重复添加的数据次数
n =  10000

# 初始化OpenAI格式的数据结构
data = [
    {
        "messages": [
            {
                "role": "user",
                "content": "请做一下自我介绍"
            },
            {
                "role": "assistant",
                "content": "我是{}的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦".format(name)
            }
        ]
    }
]

# 通过循环,将初始化的对话数据重复添加到data列表中
for i in range(n):
    data.append(data[0])

# 将data列表中的数据写入到一个名为'personal_assistant.json'的文件中
with open('personal_assistant.json', 'w', encoding='utf-8') as f:
    # 使用json.dump方法将数据以JSON格式写入文件
    # ensure_ascii=False 确保中文字符正常显示
    # indent=4 使得文件内容格式化,便于阅读
    json.dump(data, f, ensure_ascii=False, indent=4)

并将文件 name 后面的内容修改为你的名称。比如说我是“不吃西红柿”大佬的话就是:

# 将对应的name进行修改(在第4行的位置)
- name = '不要姜葱蒜大佬'
+ name = "不吃西红柿大佬"

修改完成后运行 generate_data.py 文件即可。

# 确保先进入该文件夹
cd /root/ft/data

# 运行代码
python /root/ft/data/generate_data.py

可以看到在data的路径下便生成了一个名为 personal_assistant.json 的文件,这样我们最可用于微调的数据集就准备好啦!里面就包含了 5000 条 inputoutput 的数据对。假如 我们认为 5000 条不够的话也可以调整文件中第6行 n 的值哦!

|-- data/
    |-- personal_assistant.json
    |-- generate_data.py

2.2.2 模型准备

在准备好了数据集后,接下来我们就需要准备好我们的要用于微调的模型。由于本次课程显存方面的限制,这里我们就使用 InternLM 最新推出的小模型 InterLM2-Chat-1.8B 来完成此次的微调演示。

对于在 InternStudio 上运行的小伙伴们,可以不用通过 OpenXLab 或者 Modelscope 进行模型的下载。我们直接通过以下代码一键创建文件夹并将所有文件复制进去。

# 创建目标文件夹,确保它存在。
# -p选项意味着如果上级目录不存在也会一并创建,且如果目标文件夹已存在则不会报错。
mkdir -p /root/ft/model

# 复制内容到目标文件夹。-r选项表示递归复制整个文件夹。
cp -r /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b/* /root/ft/model/

那这个时候我们就可以看到在 model 文件夹下保存了模型的相关文件和内容了。
在这里插入图片描述

2.2.3 配置文件选择

在准备好了模型和数据集后,我们就要根据我们选择的微调方法方法结合前面的信息来找到与我们最匹配的配置文件了,从而减少我们对配置文件的修改量。

所谓配置文件(config),其实是一种用于定义和控制模型训练和测试过程中各个方面的参数和设置的工具。准备好的配置文件只要运行起来就代表着模型就开始训练或者微调了。

XTuner 提供多个开箱即用的配置文件,用户可以通过下列命令查看:

开箱即用意味着假如能够连接上 Huggingface 以及有足够的显存,其实就可以直接运行这些配置文件,XTuner就能够直接下载好这些模型和数据集然后开始进行微调

# 列出所有内置配置文件
# xtuner list-cfg

# 假如我们想找到 internlm2-1.8b 模型里支持的配置文件
xtuner list-cfg -p internlm2_1_8b

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
没个1小时多是跑不出来的。。

在这里插入图片描述
加速也要半个小时才跑出来。。

2.5 模型转换、整合、测试及部署

2.5.1 模型转换

模型转换的本质其实就是将原本使用 Pytorch 训练出来的模型权重文件转换为目前通用的 Huggingface 格式文件,那么我们可以通过以下指令来实现一键转换。

# 创建一个保存转换后 Huggingface 格式的文件夹
mkdir -p /root/ft/huggingface

# 模型转换
# xtuner convert pth_to_hf ${配置文件地址} ${权重文件地址} ${转换后模型保存地址}
xtuner convert pth_to_hf /root/ft/train/internlm2_1_8b_qlora_alpaca_e3_copy.py /root/ft/train/iter_768.pth /root/ft/huggingface

转换完成后,可以看到模型被转换为 Huggingface 中常用的 .bin 格式文件,这就代表着文件成功被转化为 Huggingface 格式了。

|-- huggingface/
    |-- adapter_config.json
    |-- xtuner_config.py
    |-- adapter_model.bin
    |-- README.md

此时,huggingface 文件夹即为我们平时所理解的所谓 “LoRA 模型文件”
在这里插入图片描述

2.5.2 模型整合

我们通过视频课程的学习可以了解到,对于 LoRA 或者 QLoRA 微调出来的模型其实并不是一个完整的模型,而是一个额外的层(adapter)。那么训练完的这个层最终还是要与原模型进行组合才能被正常的使用。

而对于全量微调的模型(full)其实是不需要进行整合这一步的,因为全量微调修改的是原模型的权重而非微调一个新的 adapter ,因此是不需要进行模型整合的。
在这里插入图片描述

整合完成后可以查看在 final_model 文件夹下的内容。

|-- final_model/
    |-- tokenizer.model
    |-- config.json
    |-- pytorch_model.bin.index.json
    |-- pytorch_model-00001-of-00002.bin
    |-- tokenization_internlm2.py
    |-- tokenizer_config.json
    |-- special_tokens_map.json
    |-- pytorch_model-00002-of-00002.bin
    |-- modeling_internlm2.py
    |-- configuration_internlm2.py
    |-- tokenizer.json
    |-- generation_config.json
    |-- tokenization_internlm2_fast.py

2.5.3 对话测试

在 XTuner 中也直接的提供了一套基于 transformers 的对话代码,让我们可以直接在终端与 Huggingface 格式的模型进行对话操作。我们只需要准备我们刚刚转换好的模型路径并选择对应的提示词模版(prompt-template)即可进行对话。假如 prompt-template 选择有误,很有可能导致模型无法正确的进行回复。

想要了解具体模型的 prompt-template 或者 XTuner 里支持的 prompt-tempolate,可以到 XTuner 源码中的 xtuner/utils/templates.py 这个文件中进行查找。

# 与模型进行对话
xtuner chat /root/ft/final_model --prompt-template internlm2_chat

我们可以通过一些简单的测试来看看微调后的模型的能力。

假如我们想要输入内容需要在输入文字后敲击两下回车,假如我们想清楚历史记录需要输入 RESET,假如我们想要退出则需要输入 EXIT。

double enter to end input (EXIT: exit chat, RESET: reset history) >>> 你是谁
我是不吃西红柿大佬的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦</s>

double enter to end input (EXIT: exit chat, RESET: reset history) >>>  请你介绍一下你自己
我是不吃西红柿大佬的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦</s>

double enter to end input (EXIT: exit chat, RESET: reset history) >>> 你是我的小助手吗?
我是不吃西红柿大佬的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦</s>

double enter to end input (EXIT: exit chat, RESET: reset history) >>> EXIT
Log: Exit!

额,为什么我第一个问题“你是谁”的会回答这么多次???
在这里插入图片描述
可以看到模型已经严重过拟合,回复的话就只有 “我是不吃西红柿大佬的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦” 这句话。我们下面可以通过对比原模型的能力来看看差异。

# 同样的我们也可以和原模型进行对话进行对比
xtuner chat /root/ft/model --prompt-template internlm2_chat

我们可以用同样的问题来查看回复的情况。
在这里插入图片描述
可以看到在没有进行我们数据的微调前,原模型是能够输出有逻辑的回复,并且也不会认为他是我们特有的小助手。因此我们可以很明显的看出两者之间的差异性。

那对于 xtuner chat 这个指令而言,还有很多其他的参数可以进行设置的,包括:
在这里插入图片描述

除了这些参数以外其实还有一个非常重要的参数就是 --adapter ,这个参数主要的作用就是可以在转化后的 adapter 层与原模型整合之前来对该层进行测试。使用这个额外的参数对话的模型和整合后的模型几乎没有什么太多的区别,因此我们可以通过测试不同的权重文件生成的 adapter 来找到最优的 adapter 进行最终的模型整合工作。

# 使用 --adapter 参数与完整的模型进行对话
xtuner chat /root/ft/model --adapter /root/ft/huggingface --prompt-template internlm2_chat

在这里插入图片描述
emmm…
kimi会回答这个问题。

2.5.4 Web demo 部署

除了在终端中对模型进行测试,我们其实还可以在网页端的 demo 进行对话。

那首先我们需要先下载网页端 web demo 所需要的依赖。

pip install streamlit==1.24.0

下载 InternLM 项目代码(欢迎Star)!

# 创建存放 InternLM 文件的代码
mkdir -p /root/ft/web_demo && cd /root/ft/web_demo

# 拉取 InternLM 源文件
git clone https://github.com/InternLM/InternLM.git

# 进入该库中
cd /root/ft/web_demo/InternLM

/root/ft/web_demo/InternLM/chat/web_demo.py 中的内容替换为以下的代码(与源代码相比,此处修改了模型路径和分词器路径,并且也删除了 avatar 及 system_prompt 部分的内容,同时与 cli 中的超参数进行了对齐)。

"""This script refers to the dialogue example of streamlit, the interactive
generation code of chatglm2 and transformers.

We mainly modified part of the code logic to adapt to the
generation of our model.
Please refer to these links below for more information:
    1. streamlit chat example:
        https://docs.streamlit.io/knowledge-base/tutorials/build-conversational-apps
    2. chatglm2:
        https://github.com/THUDM/ChatGLM2-6B
    3. transformers:
        https://github.com/huggingface/transformers
Please run with the command `streamlit run path/to/web_demo.py
    --server.address=0.0.0.0 --server.port 7860`.
Using `python path/to/web_demo.py` may cause unknown problems.
"""
# isort: skip_file
import copy
import warnings
from dataclasses import asdict, dataclass
from typing import Callable, List, Optional

import streamlit as st
import torch
from torch import nn
from transformers.generation.utils import (LogitsProcessorList,
                                           StoppingCriteriaList)
from transformers.utils import logging

from transformers import AutoTokenizer, AutoModelForCausalLM  # isort: skip

logger = logging.get_logger(__name__)


@dataclass
class GenerationConfig:
    # this config is used for chat to provide more diversity
    max_length: int = 2048
    top_p: float = 0.75
    temperature: float = 0.1
    do_sample: bool = True
    repetition_penalty: float = 1.000


@torch.inference_mode()
def generate_interactive(
    model,
    tokenizer,
    prompt,
    generation_config: Optional[GenerationConfig] = None,
    logits_processor: Optional[LogitsProcessorList] = None,
    stopping_criteria: Optional[StoppingCriteriaList] = None,
    prefix_allowed_tokens_fn: Optional[Callable[[int, torch.Tensor],
                                                List[int]]] = None,
    additional_eos_token_id: Optional[int] = None,
    **kwargs,
):
    inputs = tokenizer([prompt], padding=True, return_tensors='pt')
    input_length = len(inputs['input_ids'][0])
    for k, v in inputs.items():
        inputs[k] = v.cuda()
    input_ids = inputs['input_ids']
    _, input_ids_seq_length = input_ids.shape[0], input_ids.shape[-1]
    if generation_config is None:
        generation_config = model.generation_config
    generation_config = copy.deepcopy(generation_config)
    model_kwargs = generation_config.update(**kwargs)
    bos_token_id, eos_token_id = (  # noqa: F841  # pylint: disable=W0612
        generation_config.bos_token_id,
        generation_config.eos_token_id,
    )
    if isinstance(eos_token_id, int):
        eos_token_id = [eos_token_id]
    if additional_eos_token_id is not None:
        eos_token_id.append(additional_eos_token_id)
    has_default_max_length = kwargs.get(
        'max_length') is None and generation_config.max_length is not None
    if has_default_max_length and generation_config.max_new_tokens is None:
        warnings.warn(
            f"Using 'max_length''s default ({repr(generation_config.max_length)}) \
                to control the generation length. "
            'This behaviour is deprecated and will be removed from the \
                config in v5 of Transformers -- we'
            ' recommend using `max_new_tokens` to control the maximum \
                length of the generation.',
            UserWarning,
        )
    elif generation_config.max_new_tokens is not None:
        generation_config.max_length = generation_config.max_new_tokens + \
            input_ids_seq_length
        if not has_default_max_length:
            logger.warn(  # pylint: disable=W4902
                f"Both 'max_new_tokens' (={generation_config.max_new_tokens}) "
                f"and 'max_length'(={generation_config.max_length}) seem to "
                "have been set. 'max_new_tokens' will take precedence. "
                'Please refer to the documentation for more information. '
                '(https://huggingface.co/docs/transformers/main/'
                'en/main_classes/text_generation)',
                UserWarning,
            )

    if input_ids_seq_length >= generation_config.max_length:
        input_ids_string = 'input_ids'
        logger.warning(
            f"Input length of {input_ids_string} is {input_ids_seq_length}, "
            f"but 'max_length' is set to {generation_config.max_length}. "
            'This can lead to unexpected behavior. You should consider'
            " increasing 'max_new_tokens'.")

    # 2. Set generation parameters if not already defined
    logits_processor = logits_processor if logits_processor is not None \
        else LogitsProcessorList()
    stopping_criteria = stopping_criteria if stopping_criteria is not None \
        else StoppingCriteriaList()

    logits_processor = model._get_logits_processor(
        generation_config=generation_config,
        input_ids_seq_length=input_ids_seq_length,
        encoder_input_ids=input_ids,
        prefix_allowed_tokens_fn=prefix_allowed_tokens_fn,
        logits_processor=logits_processor,
    )

    stopping_criteria = model._get_stopping_criteria(
        generation_config=generation_config,
        stopping_criteria=stopping_criteria)
    logits_warper = model._get_logits_warper(generation_config)

    unfinished_sequences = input_ids.new(input_ids.shape[0]).fill_(1)
    scores = None
    while True:
        model_inputs = model.prepare_inputs_for_generation(
            input_ids, **model_kwargs)
        # forward pass to get next token
        outputs = model(
            **model_inputs,
            return_dict=True,
            output_attentions=False,
            output_hidden_states=False,
        )

        next_token_logits = outputs.logits[:, -1, :]

        # pre-process distribution
        next_token_scores = logits_processor(input_ids, next_token_logits)
        next_token_scores = logits_warper(input_ids, next_token_scores)

        # sample
        probs = nn.functional.softmax(next_token_scores, dim=-1)
        if generation_config.do_sample:
            next_tokens = torch.multinomial(probs, num_samples=1).squeeze(1)
        else:
            next_tokens = torch.argmax(probs, dim=-1)

        # update generated ids, model inputs, and length for next step
        input_ids = torch.cat([input_ids, next_tokens[:, None]], dim=-1)
        model_kwargs = model._update_model_kwargs_for_generation(
            outputs, model_kwargs, is_encoder_decoder=False)
        unfinished_sequences = unfinished_sequences.mul(
            (min(next_tokens != i for i in eos_token_id)).long())

        output_token_ids = input_ids[0].cpu().tolist()
        output_token_ids = output_token_ids[input_length:]
        for each_eos_token_id in eos_token_id:
            if output_token_ids[-1] == each_eos_token_id:
                output_token_ids = output_token_ids[:-1]
        response = tokenizer.decode(output_token_ids)

        yield response
        # stop when each sentence is finished
        # or if we exceed the maximum length
        if unfinished_sequences.max() == 0 or stopping_criteria(
                input_ids, scores):
            break


def on_btn_click():
    del st.session_state.messages


@st.cache_resource
def load_model():
    model = (AutoModelForCausalLM.from_pretrained('/root/ft/final_model',
                                                  trust_remote_code=True).to(
                                                      torch.bfloat16).cuda())
    tokenizer = AutoTokenizer.from_pretrained('/root/ft/final_model',
                                              trust_remote_code=True)
    return model, tokenizer


def prepare_generation_config():
    with st.sidebar:
        max_length = st.slider('Max Length',
                               min_value=8,
                               max_value=32768,
                               value=2048)
        top_p = st.slider('Top P', 0.0, 1.0, 0.75, step=0.01)
        temperature = st.slider('Temperature', 0.0, 1.0, 0.1, step=0.01)
        st.button('Clear Chat History', on_click=on_btn_click)

    generation_config = GenerationConfig(max_length=max_length,
                                         top_p=top_p,
                                         temperature=temperature)

    return generation_config


user_prompt = '<|im_start|>user\n{user}<|im_end|>\n'
robot_prompt = '<|im_start|>assistant\n{robot}<|im_end|>\n'
cur_query_prompt = '<|im_start|>user\n{user}<|im_end|>\n\
    <|im_start|>assistant\n'


def combine_history(prompt):
    messages = st.session_state.messages
    meta_instruction = ('')
    total_prompt = f"<s><|im_start|>system\n{meta_instruction}<|im_end|>\n"
    for message in messages:
        cur_content = message['content']
        if message['role'] == 'user':
            cur_prompt = user_prompt.format(user=cur_content)
        elif message['role'] == 'robot':
            cur_prompt = robot_prompt.format(robot=cur_content)
        else:
            raise RuntimeError
        total_prompt += cur_prompt
    total_prompt = total_prompt + cur_query_prompt.format(user=prompt)
    return total_prompt


def main():
    # torch.cuda.empty_cache()
    print('load model begin.')
    model, tokenizer = load_model()
    print('load model end.')


    st.title('InternLM2-Chat-1.8B')

    generation_config = prepare_generation_config()

    # Initialize chat history
    if 'messages' not in st.session_state:
        st.session_state.messages = []

    # Display chat messages from history on app rerun
    for message in st.session_state.messages:
        with st.chat_message(message['role'], avatar=message.get('avatar')):
            st.markdown(message['content'])

    # Accept user input
    if prompt := st.chat_input('What is up?'):
        # Display user message in chat message container
        with st.chat_message('user'):
            st.markdown(prompt)
        real_prompt = combine_history(prompt)
        # Add user message to chat history
        st.session_state.messages.append({
            'role': 'user',
            'content': prompt,
        })

        with st.chat_message('robot'):
            message_placeholder = st.empty()
            for cur_response in generate_interactive(
                    model=model,
                    tokenizer=tokenizer,
                    prompt=real_prompt,
                    additional_eos_token_id=92542,
                    **asdict(generation_config),
            ):
                # Display robot response in chat message container
                message_placeholder.markdown(cur_response + '▌')
            message_placeholder.markdown(cur_response)
        # Add robot response to chat history
        st.session_state.messages.append({
            'role': 'robot',
            'content': cur_response,  # pylint: disable=undefined-loop-variable
        })
        torch.cuda.empty_cache()


if __name__ == '__main__':
    main()

在运行前,我们还需要做的就是将端口映射到本地。那首先我们使用快捷键组合 Windows + R(Windows 即开始菜单键)打开指令界面,并输入命令,按下回车键。(Mac 用户打开终端即可)

# 从本地使用 ssh 连接 studio 端口
# 将下方端口号 38374 替换成自己的端口号
ssh -CNg -L 6006:127.0.0.1:6006 root@ssh.intern-ai.org.cn -p 38374

之后我们需要输入以下命令运行 /root/personal_assistant/code/InternLM 目录下的 web_demo.py 文件。

streamlit run /root/ft/web_demo/InternLM/chat/web_demo.py --server.address 127.0.0.1 --server.port 6006

注意:要在浏览器打开 http://127.0.0.1:6006 页面后,模型才会加载。

打开 http://127.0.0.1:6006 后,等待加载完成即可进行对话,键入内容示例如下:

请介绍一下你自己

在这里插入图片描述

2.5.5 小结

在这一小节里我们对微调后的模型(adapter)进行了转换及整合的操作,并通过 xtuner chat 来对模型进行了实际的对话测试。从结果可以清楚的看出模型的回复在微调的前后出现了明显的变化。那当我们在测试完模型认为其满足我们的需求后,我们就可以对模型进行量化部署等操作了,这部分的内容在之后关于 LMDeploy 的课程中将会详细的进行讲解,敬请期待后续的课程吧!

2.6 总结

在本节中主要就是带领着大家跑通了 XTuner 的一个完整流程,通过了解数据集和模型的使用方法、配置文件的制作和训练以及最后的转换及整合。那在后面假如我们也有想要微调出自己的一个模型,我们也可以尝试使用同样流程和方法进行进一步的实践!

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

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

相关文章

SQL刷题---2021年11月每天新用户的次日留存率

解题思路&#xff1a; 1.首先算出每个新用户注册的日期,将其命名为表a select uid,min(date(in_time)) dt from tb_user_log group by uid2.计算出每个用户登录的天数,将其命名为表b select uid,date(in_time) dt from tb_user_log union select uid,date(out_time) dt fro…

linux C -- 消息队列

linux C -- 消息队列 前言一、System V(IPC)消息队列接口调用主要涉及到 msgget、msgsnd、msgrcv 和 msgctl 四个接口&#xff1a; 1、创建消息队列 msgget2、发送消息到队列3、从队列接收信息4、控制消息队列 msgctl5、删除消息队列 二、代码编写1、发送部分的代码2、代码完成…

扭蛋机市场如何?全新淘宝扭蛋机小程序发展前景

近几年&#xff0c;扭蛋机市场发展的非常迅速&#xff0c;市场发展前景也在不断扩大。随着人们的生活水平提高&#xff0c;对娱乐消费也更加青睐&#xff0c;尤其是具有刺激性、惊喜性的消费模式。而扭蛋机具备的优势刚好符合大众对娱乐消费的要求&#xff0c;因此&#xff0c;…

Kafka服务端(含Zookeeper)一键自启软件

1. 前言 本文介绍了一款集成图形化界面配置和一键自启功能的Kafka与Zookeeper服务管理软件。该软件通过直观易用的图形界面&#xff0c;使用户能够轻松完成Kafka和Zookeeper的配置工作&#xff0c;有效避免了手动编辑配置文件可能带来的错误和不便。同时&#xff0c;软件还提供…

TCP网络程序

上一章我们基于UDP实现了几个网络程序&#xff0c;这一章我们开始使用TCP。 先简单复习一下TCP和UDP的特点&#xff1a; TCP特点 传输层协议有连接可靠传输面向字节流 UDP特点 传输层协议无连接不可靠传输面向数据报 可以看到TCP是有链接的&#xff0c;而UDP是无连接的&#…

简单3步,OpenHarmony上跑起ArkUI分布式小游戏

标准系统新增支持了方舟开发框架&#xff08;ArkUI&#xff09;、分布式组网和 FA 跨设备迁移能力等新特性&#xff0c;因此我们结合了这三种特性使用 ets 开发了一款如下动图所示传炸弹应用。 打开应用在通过邀请用户进行设备认证后&#xff0c;用户须根据提示完成相应操作&am…

2024-14.python前端+Django

第四篇 web前端 第1章 、Web的基本概念 前端基础总共分为三部分&#xff1a;html、css和js。 1.3、HTTP协议 1.3.1 、http协议简介 HTTP协议是Hyper Text Transfer Protocol&#xff08;超文本传输协议&#xff09;的缩写,是用于万维网&#xff08;WWW:World Wide Web &am…

神经网络压缩图像

简介 典型的压缩管道由四个组件组成&#xff1a; 编码&#xff1a;输入图像 x x x通过编码器函数 ε \varepsilon ε&#xff0c;将其转换为潜在表示 z z z。 量化&#xff1a;截断 z z z以丢弃一些不重要的信息 熵编码&#xff1a;使用某种形式的熵编码&#xff08;例如&…

淘宝API商品详情数据在数据分析行业中具有不可忽视的重要性

淘宝商品详情数据在数据分析行业中具有不可忽视的重要性。这些数据为商家、市场分析师以及数据科学家提供了丰富的信息&#xff0c;有助于他们更深入地理解市场动态、消费者行为以及商品竞争态势。以下是淘宝商品详情数据在数据分析行业中的重要性体现&#xff1a; 请求示例&a…

OpenStack 入门体验

目录 一、云计算概述 1.1、什么是云计算 1.2、云计算的服务模型 1&#xff09;IaaS 2&#xff09;PaaS 3&#xff09;SaaS 1.3、OpenStack 概述 1&#xff09;OpenStack 起源 2&#xff09;什么是 OpenStack 3&#xff09;OpenStack 优势 二、OpenStack 一…

基于JavaWeb开发的springboot网约车智能接单规划小程序[附源码]

基于JavaWeb开发的springboot网约车智能接单规划小程序[附源码] &#x1f345; 作者主页 央顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; &#x1f345; 查看下方微信号获取联系方式 承接各种…

Java项目如何使用EasyExcel插件对Excel数据进行导入导出

文章目录 一、EasyExcel的示例导入依赖创建实体类数据导入和导出 二、EasyExcel的作用三、EasyExcel的注解 EasyExcel是一个阿里巴巴开源的excel处理框架&#xff0c;它以使用简单、节省内存著称。在解析Excel时&#xff0c;EasyExcel没有将文件数据一次性全部加载到内存中&…

使用 Flask 和 Flask-Login 构建用户认证的 Web 应用程序

在本篇技术博客中&#xff0c;我们将学习如何使用 Flask 框架和 Flask-Login 扩展构建一个具有用户认证功能的简单 Web 应用程序。我们将从创建 Flask 应用实例开始&#xff0c;然后逐步添加用户认证功能。 1. 安装依赖库 首先&#xff0c;确保您已经安装了 Flask、Flask-PyM…

大模型微调的几种常见方法

在文章深入理解大语言模型微调技术中&#xff0c;我们详细了解大语言模型微调的概念和训练过程&#xff0c;本篇给大家介绍大模型微调常见的7种训练方法。 1、Adapter Tuning 2019年谷歌的研究人员首次在论文《Parameter-Efficient Transfer Learning for NLP》提出针对 BERT 的…

2024年Q1季度空调行业线上市场销售数据分析

Q1季度一直以来就是空调行业的淡季&#xff08;旺季一般出现在5月至7月&#xff09;。而今年&#xff0c;空调线上市场低迷发展的态势越发明显。 根据鲸参谋数据显示&#xff0c;2024年1月至3月线上电商平台&#xff08;京东天猫淘宝&#xff09;空调累计销量约270万件&#x…

开源 Ruo-Yi 项目引入 Mybatis-Plus:3.5.3 报错ClassNotFoundException:

开源 Ruo-Yi 项目引入 Mybatis-Plus:3.5.3 报错ClassNotFoundException&#xff1a; Caused by: java.lang.ClassNotFoundException: com.baomidou.mybatisplus.extension.plugins.MybatisPlusInter1 分析问题 控制台报错说明我们引入的 mybatis-plus 的依赖里找不到com.baom…

Apache Tomcat 简单使用

Apache Tomcat 下载 download Tomcat服务器是一个免费的开放源代码的Web应用服务器&#xff0c;属于轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多的场合下被普遍使用&#xff0c;是开发和调试JSP程序的首选。 支持JDK版本 JDK8 要下载tomcat 9.0.x版本 …

【云计算】混合云分类

《混合云》系列&#xff0c;共包含以下 3 篇文章&#xff1a; 【云计算】混合云概述【云计算】混合云分类【云计算】混合云组成、应用场景、风险挑战 &#x1f60a; 如果您觉得这篇文章有用 ✔️ 的话&#xff0c;请给博主一个一键三连 &#x1f680;&#x1f680;&#x1f68…

Samtec应用分享 | 汽车应用中的视觉系统

【前言】 视觉系统在未来的汽车设计中扮演着关键的角色。 在过去&#xff0c;一直是由驾驶员掌握和应对道路上的危险&#xff0c;但现代车辆在保障驾驶安全方面发挥着前所未有的作用。 视觉系统&#xff0c;无论是可见光摄像头还是先进的探测系统&#xff0c;如激光雷达&…

分类损失函数与评估指标

目录 1 评估指标 1.1 准确率 1.2 精确率 1.3 召回率 1.4 F1 score 1.5 ROC曲线 1.6 AUC 1.7 PRC曲线的优势 2 损失函数 1. 负对数似然损失 2. 交叉熵损失 3. 指数损失 3 分类问题为什么用交叉熵损失不用 MSE 损失 1 评估指标 混淆矩阵 TP(True Positive) ---- 正…