lora微调Qwen模型全流程

news2024/9/17 7:19:58

LoRA 微调 Qwen 模型的技术原理概述

LoRA(Low-Rank Adaptation)是一种用于大模型高效微调的方法。通过对模型参数进行低秩分解和特定层的微调,LoRA 能在保持模型性能的前提下显著减少训练所需的参数量和计算资源。接下来是对 LoRA 微调 Qwen 模型的完整技术流程概述:

  1. 模型和分词器加载
    首先,从预训练模型库中加载预训练的 Qwen 模型和分词器。预训练模型是大规模语料上训练的通用语言模型,能够为特定任务提供强大的语言理解和生成能力。

  2. 配置 LoRA
    定义 LoRA 配置,包括任务类型、目标模块、秩(rank)、alpha 参数和 dropout 比例等。LoRA 通过在特定层中引入低秩矩阵,减少需要微调的参数数量。具体配置如下:

任务类型:因果语言模型(Causal Language Model)。
目标模块:指定模型中的哪些模块将应用 LoRA,例如 Transformer 层中的投影矩阵。
秩和 alpha 参数:控制低秩矩阵的大小和缩放因子。
Dropout 比例:用于正则化,防止过拟合。
3. 加载数据集
使用适当的工具加载和处理数据集。数据集通常以 JSON 格式存储,包含指令、输入和预期输出。数据预处理步骤包括对文本进行标记化(tokenization)、生成输入 ID 和注意力掩码,并构建适用于模型的输入格式。

  1. 数据加载器
    创建数据加载器(DataLoader),用于批量处理数据,以便在训练过程中有效地喂入模型。这一步有助于优化训练效率和内存管理。

  2. 微调模型
    进行模型微调,即在特定任务数据集上进一步训练预训练模型。使用优化算法(如 AdamW)和适当的学习率,针对特定任务调整模型参数。通过 LoRA 技术,仅更新一小部分参数,大大减少了计算开销。

  3. 保存微调后的模型
    将微调后的模型和分词器保存到本地,以便后续加载和使用。这一步确保了模型的可重用性和部署的便利性。

  4. 模型生成
    加载微调后的模型和分词器,输入新的指令和上下文,进行文本生成。微调后的模型能够根据特定任务的需求,生成更符合预期的输出。

技术优势
参数高效:LoRA 通过低秩分解,仅微调少量参数,大幅减少了训练和推理的计算资源需求。
性能保持:尽管微调的参数数量减少,LoRA 依然能够保持模型的性能,适用于大多数语言任务。
灵活性高:LoRA 配置灵活,可以根据不同任务和模型架构进行调整,广泛适用于各类深度学习模型。
通过以上技术流程,LoRA 微调实现了对 Qwen 模型的高效优化,使其能够在特定任务上表现优异,同时显著降低了计算成本。

代码实现

  • 安装依赖
python -m pip install --upgrade pip
# 更换 pypi 源加速库的安装
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

pip install modelscope==1.9.5
pip install "transformers>=4.39.0"
pip install streamlit==1.24.0
pip install sentencepiece==0.1.99
pip install accelerate==0.27
pip install transformers_stream_generator==0.0.4
pip install datasets==2.18.0
pip install peft==0.10.0

# 可选
MAX_JOBS=8 pip install flash-attn --no-build-isolation 
  • 数据准备
    整体流程是加载和合并不同领域的数据,添加领域标识后进行数据采样,拆分为训练集和测试集,最后保存并可视化数据分布。这样可以确保在不同领域的数据中进行均匀采样和合理拆分,有助于模型的训练和测试。
import json
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split

# 加载JSON文件
def load_json(file_path):
    with open(file_path, 'r', encoding='utf-8') as f:
        data = json.load(f)
    return data

# 合并不同领域的数据
domain1_data = load_json('xiyouji.json')
domain2_data = load_json('zhenhuanzhuan.json')
# domain3_data = load_json('domain3.json')

# 为每个数据添加领域标识
for item in domain1_data:
    item['domain'] = 'domain1'
for item in domain2_data:
    item['domain'] = 'domain2'
# for item in domain3_data:
    # item['domain'] = 'domain3'

# 合并数据
all_data = domain1_data + domain2_data
# 将数据转换为DataFrame以便于操作和可视化
df = pd.DataFrame(all_data)

# 可视化数据分布
def visualize_data_distribution(df, title, file_name, domain_column='domain'):
    plt.figure(figsize=(10, 6))
    sns.countplot(data=df, x=domain_column)
    plt.title(title)
    plt.savefig(file_name)
    plt.show()

# 可视化初始数据分布
visualize_data_distribution(df, "Initial Data Distribution Across Domains", "initial_data_distribution.png")

# 对每个领域进行采样(每个领域采样20条数据)
sampled_df = df.groupby('domain').apply(lambda x: x.sample(n=20, random_state=42)).reset_index(drop=True)

# 可视化采样后的数据分布
visualize_data_distribution(sampled_df, "Sampled Data Distribution Across Domains", "sampled_data_distribution.png")

# 将采样后的数据制作成训练集和测试集
train_df, test_df = train_test_split(sampled_df, test_size=0.2, random_state=42)

# 保存采样后的训练集和测试集
train_df.to_json('train_dataset.json', orient='records', force_ascii=False, lines=True)
test_df.to_json('test_dataset.json', orient='records', force_ascii=False, lines=True)

# 检查采样后的数据分布
visualize_data_distribution(train_df, "Train Data Distribution After Sampling", "train_data_distribution.png")
visualize_data_distribution(test_df, "Test Data Distribution After Sampling", "test_data_distribution.png")
  • 导入依赖
from datasets import Dataset
import pandas as pd
from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer, GenerationConfig
  • 下载模型
    使用 modelscope 中的 snapshot_download 函数下载模型。
#模型下载
from modelscope import snapshot_download
model_dir = snapshot_download('qwen/Qwen1.5-0.5B')
print(f"Model downloaded to: {model_dir}")

输出结果:
在这里插入图片描述

  • 加载数据,查看
# 数据加载,查看
from datasets import load_dataset
# 加载json格式的训练数据集
_dataset = load_dataset("json", data_files="train_dataset.json", split="train")
_dataset

在这里插入图片描述

数据集特征
特征结构:数据集包含 instruction(指令)、input(输入)、output(输出)和 domain(领域)。这些特征是合理的,适合用于训练一个角色扮演的对话模型。

  • instruction:模型应该遵循的指令。
  • input:用户输入。
  • output:模型期望生成的输出。
  • domain:数据的领域标识,可能用于多任务学习或领域适应。
加载分词器模型

加载本地的Qwen1.5-0.5B模型

tokenizer = AutoTokenizer.from_pretrained('/root/.cache/modelscope/hub/qwen/Qwen1___5-0___5B', use_fast=False, trust_remote_code=True)
tokenizer
数据格式化处理

Lora 训练的数据是需要经过格式化、编码之后再输入给模型进行训练的,我们一般需要将输入文本编码为 input_ids,将输出文本编码为 labels,编码之后的结果都是多维的向量。我们首先定义一个预处理函数,这个函数用于对每一个样本,编码其输入、输出文本并返回一个编码后的字典。

  • 定义处理函数
def process_func(example):
    MAX_LENGTH = 384    # Llama分词器会将一个中文字切分为多个token,因此需要放开一些最大长度,保证数据的完整性
    input_ids, attention_mask, labels = [], [], []
    instruction = tokenizer(f"<|im_start|>system\n现在你要充当一名角色扮演者。<|im_end|>\n<|im_start|>user\n{example['instruction'] + example['input']}<|im_end|>\n<|im_start|>assistant\n", add_special_tokens=False)  # add_special_tokens 不在开头加 special_tokens
    print(instruction)
    response = tokenizer(f"{example['output']}", add_special_tokens=False)
    input_ids = instruction["input_ids"] + response["input_ids"] + [tokenizer.pad_token_id]
    attention_mask = instruction["attention_mask"] + response["attention_mask"] + [1]  # 因为eos token咱们也是要关注的所以 补充为1
    labels = [-100] * len(instruction["input_ids"]) + response["input_ids"] + [tokenizer.pad_token_id]  
    if len(input_ids) > MAX_LENGTH:  # 做一个截断
        input_ids = input_ids[:MAX_LENGTH]
        attention_mask = attention_mask[:MAX_LENGTH]
        labels = labels[:MAX_LENGTH]
    return {
        "input_ids": input_ids,
        "attention_mask": attention_mask,
        "labels": labels
    }

单纯的一问一答的数据集格式像这样的:

 {
        "case": "患者李某,男,45岁,最近患病毒性感冒,初起恶寒发热,继而身热持续不退,频转矢气,胸脘痞满,腹部胀痛拒按,大便秘结,数日未行,咽痛口苦,口舌生疮,头痛目赤。潮热谵语,小便短赤,舌苔厚黄,舌生芒刺,脉沉实数。此患者主症为大便秘结,腹部胀痛拒按,以及其他症状包括身热、频转矢气、胸脘痞满、潮热、谵语、小便短赤、咽痛、口舌生疮、头痛、目赤、舌苔厚黄、舌生芒刺、脉沉实。根据这些症状的描述,我们可以推断出该患者可能患有病毒性感冒。",
        "diagnosis": "诊断:阳明热结。建议处方:小承气汤。建议中成药:厚朴排气合剂或牛黄清胃丸"
    }

数据处理的部分代码就需要改一下:

def process_func(example):
    MAX_LENGTH = 384  # Llama分词器会将一个中文字切分为多个token,因此需要放开一些最大长度,保证数据的完整性
    
    # 构建instruction和response的token化结果
    instruction_text = f"system\n现在你要充当一名角色扮演者。\nuser\n{example['case']}\nassistant\n"
    response_text = example['diagnosis']

    instruction = tokenizer(instruction_text, add_special_tokens=False)
    response = tokenizer(response_text, add_special_tokens=False)

    input_ids = instruction["input_ids"] + response["input_ids"] + [tokenizer.pad_token_id]
    attention_mask = instruction["attention_mask"] + response["attention_mask"] + [1]  # eos token也需要关注
    labels = [-100] * len(instruction["input_ids"]) + response["input_ids"] + [tokenizer.pad_token_id]

    if len(input_ids) > MAX_LENGTH:
        input_ids = input_ids[:MAX_LENGTH]
        attention_mask = attention_mask[:MAX_LENGTH]
        labels = labels[:MAX_LENGTH]

    return {
        "input_ids": input_ids,
        "attention_mask": attention_mask,
        "labels": labels
    }

  • 数据集处理
tokenized_id = _dataset.map(process_func, remove_columns=_dataset.column_names)
tokenized_id

在这里插入图片描述

  • 加载模型
import torch

model = AutoModelForCausalLM.from_pretrained('/root/.cache/modelscope/hub/qwen/Qwen1___5-0___5B', device_map="auto",torch_dtype=torch.bfloat16)
model

在这里插入图片描述

  • 开启梯度检查,查看精度
model.enable_input_require_grads() # 开启梯度检查点时,要执行该方法
model.dtype # 查看精度
lora配置
from peft import LoraConfig, TaskType, get_peft_model

config = LoraConfig(
    task_type=TaskType.CAUSAL_LM, 
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
    inference_mode=False, # 训练模式
    r=8, # Lora 秩
    lora_alpha=32, # Lora alaph,具体作用参见 Lora 原理
    lora_dropout=0.1# Dropout 比例
)
config
  • task_type=TaskType.CAUSAL_LM:指定任务类型为因果语言模型(Causal Language Model)。这表明该配置用于训练一个自回归语言模型。
  • target_modules=[“q_proj”, “k_proj”, “v_proj”, “o_proj”, “gate_proj”, “up_proj”, “down_proj”]:指定要应用 LoRA 的目标模块列表。这里列出了常见的变压器(Transformer)模块,如查询投影(q_proj)、键投影(k_proj)、值投影(v_proj)等。
  • inference_mode=False:指定当前模式为训练模式(False),而非推理模式(True)。这意味着模型会进行训练更新。
  • r=8:指定 LoRA 的秩(rank)。这是低秩分解的秩值,表示要将参数矩阵分解为两个秩为 8 的矩阵。
  • lora_alpha=32:LoRA 的缩放因子。用于调整低秩分解后矩阵的值的范围。
  • lora_dropout=0.1:Dropout 比例。表示在应用 LoRA 的层中,10% 的神经元会被随机丢弃以防止过拟合。
加载lora
model = get_peft_model(model, config)
config
  • 查看可训练参数
model.print_trainable_parameters()
配置训练参数
args = TrainingArguments(
    output_dir="./output/Qwen1___5-0___5B",
    per_device_train_batch_size=64,
    gradient_accumulation_steps=4,
    logging_steps=10,
    num_train_epochs=100,
    save_steps=100, # 建议你置成100
    learning_rate=1e-4,
    save_on_each_node=True,
    gradient_checkpointing=True
)

参数解释:

  • output_dir:
    output_dir=“./output/Qwen1___5-0___5B”
    指定训练过程中保存模型和其他输出文件的目录。所有的检查点和最终的模型将保存在这个目录中。
  • per_device_train_batch_size:
    per_device_train_batch_size=64
    每个设备(如 GPU 或 CPU)的训练批处理大小。在分布式训练或多 GPU 训练中,这个参数指的是每个设备上的批处理大小。
  • gradient_accumulation_steps:
    gradient_accumulation_steps=4
    梯度累积步数。此参数允许在多次前向传递后再进行一次梯度更新,以模拟更大的批处理大小。例如,如果设置为 4,则会在 4 个批次之后进行一次梯度更新,相当于将实际的批处理大小乘以 4。
  • logging_steps:
    logging_steps=10
    指定记录日志的频率(以步数为单位)。每 10 步会记录一次日志信息,包括训练损失和其他指标。
  • num_train_epochs:
    num_train_epochs=100
    训练的总轮数。模型将在整个训练集上训练 100 个周期。
  • save_steps:
    save_steps=100
    指定保存模型检查点的频率(以步数为单位)。每 100 步会保存一次模型检查点。这对于长时间训练的过程非常有用,可以在训练过程中定期保存模型。
  • learning_rate:
    learning_rate=1e-4
    训练过程中使用的学习率。学习率控制了模型参数更新的步幅,是训练中最重要的超参数之一。
  • save_on_each_node:
    save_on_each_node=True
    在分布式训练中,是否在每个节点上保存检查点。设置为 True 可以确保在分布式环境下每个节点都会保存检查点。
  • gradient_checkpointing:
    gradient_checkpointing=True
    启用梯度检查点,可以在训练大模型时节省显存。启用该功能后,模型在计算反向传播时会逐层计算和保存梯度,而不是一次性全部计算和保存。
模型训练
训练之前检查并释放GPU
def check_and_release_gpu_memory():
    # 检查每个 GPU 的显存使用情况
    for i in range(torch.cuda.device_count()):
        print(f"Device {i}: {torch.cuda.get_device_name(i)}")
        print(f"  Total Memory: {torch.cuda.get_device_properties(i).total_memory / (1024 ** 3):.2f} GB")
        print(f"  Allocated: {torch.cuda.memory_allocated(i) / (1024 ** 3):.2f} GB")
        print(f"  Cached: {torch.cuda.memory_reserved(i) / (1024 ** 3):.2f} GB")
    
    # 释放所有 GPU 的显存
    torch.cuda.empty_cache()
    print("All GPU memory caches have been released.")

# 调用函数检查和释放显存
check_and_release_gpu_memory()
trainer = Trainer(
    model=model,
    args=args,
    train_dataset=tokenized_id,
    data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),
)
trainer.train()

在这里插入图片描述

模型合并

将训练后的lora权重加载到原来的模型中,形成新的模型。并保存带本地。

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
from peft import PeftModel

mode_path = '/root/.cache/modelscope/hub/qwen/Qwen1___5-0___5B'
lora_path = './output/Qwen1___5-0___5B/checkpoint-100' # 这里改称你的 lora 输出对应 checkpoint 地址

# 加载tokenizer
tokenizer = AutoTokenizer.from_pretrained(mode_path, trust_remote_code=True)

# 加载模型
model = AutoModelForCausalLM.from_pretrained(mode_path, device_map="auto", torch_dtype=torch.bfloat16, trust_remote_code=True).eval()

# 加载lora权重
model = PeftModel.from_pretrained(model, model_id=lora_path)

# 保存合并后的模型和分词器
save_path = './output/merged_model'
model.save_pretrained(save_path)
tokenizer.save_pretrained(save_path)

模型推理

基于合并后(加载了lora权重)的模型进行推理

# 现在加载合并后的模型和分词器进行生成
model = AutoModelForCausalLM.from_pretrained(save_path, device_map="auto", torch_dtype=torch.bfloat16).eval()
tokenizer = AutoTokenizer.from_pretrained(save_path)

# 准备生成输入
prompt = "悟空,我们的干粮不多了,接下来该怎么办?"
messages = [
    {"role": "system", "content": "现在你要充当一名角色扮演者。"},
    {"role": "user", "content": prompt}
]

# 使用分词器准备输入
inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=True, return_tensors="pt", return_dict=True).to('cuda')

# 生成设置
gen_kwargs = {"max_length": 2500, "do_sample": True, "top_k": 1}

# 生成文本
with torch.no_grad():
    outputs = model.generate(**inputs, **gen_kwargs)
    outputs = outputs[:, inputs['input_ids'].shape[1]:]
    print(tokenizer.decode(outputs[0], skip_special_tokens=True))

在这里插入图片描述

  • 使用pipeline运行:
from transformers import pipeline, AutoModelForCausalLM, AutoTokenizer

# 加载合并后的模型和分词器
save_path = "myqwen2-0.5b"  # 根据实际保存路径进行调整
model = AutoModelForCausalLM.from_pretrained(save_path, torch_dtype=torch.bfloat16).eval()
tokenizer = AutoTokenizer.from_pretrained(save_path)

# 准备生成输入
prompt = "悟空,我们的干粮不多了,接下来该怎么办?"
messages = [
    {"role": "system", "content": "现在你要充当一名角色扮演者。"},
    {"role": "user", "content": prompt}
]

# 将消息转化为输入字符串
input_text = " ".join([msg["content"] for msg in messages])

# 使用pipeline进行文本生成
text_generation_pipeline = pipeline("text-generation", model=model, tokenizer=tokenizer)

# 生成设置
gen_kwargs = {"max_length": 256, "do_sample": True, "top_k": 1, "temperature": 0.7}

# 生成文本
outputs = text_generation_pipeline(input_text, **gen_kwargs)

# 输出生成的文本
print(outputs[0]['generated_text'])

或者:

from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM
import pandas as pd

# 加载合并后的模型和分词器
save_path = "myqwen2-0.5b"  # 根据实际保存路径进行调整
model = AutoModelForCausalLM.from_pretrained(save_path, torch_dtype=torch.bfloat16).eval()
tokenizer = AutoTokenizer.from_pretrained(save_path)

# 创建文本生成Pipeline
text_generator = pipeline("text-generation", model=model, tokenizer=tokenizer)

# 准备测试数据
test_data = pd.DataFrame({
    "prompt": ["悟空,我们的干粮不多了,接下来该怎么办?"]
})

# 对测试数据进行预测
for i in range(len(test_data)):
    prompt = test_data["prompt"][i]
    generated_text = text_generator(
        prompt, 
        max_length=100,  # 增加生成长度
        do_sample=True, 
        top_k=50,       # 增加多样性
        top_p=0.95,     # 增加多样性
        temperature=0.7,  # 调整生成温度
        repetition_penalty=1.2  # 加入重复惩罚
    )
    print(f"Prompt: {prompt}")
    print(f"Generated Text: {generated_text[0]['generated_text']}")

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Matlab编程资源库(9)数据插值与曲线拟合

一、一维数据插值 在MATLAB中&#xff0c;实现这些插值的函数是interp1&#xff0c;其调用格式为&#xff1a; Y1interp1(X,Y,X1,method) 函数根据X,Y的值&#xff0c;计算函数在X1处的值。X,Y是两个等长的已知向量&#xff0c;分别描述采样点和样本值&#xff0c;X1是一个向量…

【机器学习基础】初探机器学习

【作者主页】Francek Chen 【专栏介绍】⌈Python机器学习⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;依赖于强大的开…

vue项目引入live2d保姆级教程--web端、多种方法

一、自建live2d运行 1、选择SDK——live2d Cubism SDK &#xff08;1&#xff09;链接&#xff1a;Live2D Cubism SDK | Live2D Cubism 打开网站&#xff0c;它长这样&#xff1a; &#xff08;2&#xff09;选择web &#xff0c;到下个页面 &#xff08;3&#xff09;下…

Power Tower

Problem - D - Codeforces 牛客和codeforce都有 递归处理l,r&#xff0c;终点是lr && mod1 用扩展欧拉定理 // Problem: D. Power Tower // Contest: Codeforces - Codeforces Round 454 (Div. 1, based on Technocup 2018 Elimination Round 4) // URL: https://c…

Linux基础操作指令

Linux的操作特点&#xff1a;纯命令行&#xff08;虽然也有图形化界面&#xff0c;但主要是工程师使用&#xff0c;意义不大&#xff09; windows的操作特点&#xff1a;图形化界面&#xff08;也有纯命令行的形式&#xff0c;但其更贴近大众&#xff0c;命令行学习成本高&…

用Python打造精彩动画与视频1.2 安装Python和基本配置

1.2 安装Python和基本配置 在本章节中&#xff0c;我们将介绍如何在不同操作系统上安装Python&#xff0c;并进行基本配置&#xff0c;以便你能够顺利开始使用Python进行编程和多媒体创作。 1.2.1 Python的安装 Python有多个版本&#xff0c;目前主要使用Python 3版本。以下…

贪心加暴力枚举

数据范围只有 1 0 5 10^5 105所以我们可以直接暴力枚举&#xff0c;然后我们知道假操作一定是在最开始进行&#xff0c;至于加多少次我们可以直接枚举 class Solution { public:int minOperations(int k) {// 只可能先加后叠加if (k 1) return 0;int ans 0x7ffffff;int now…

Windows FreeCAD 导入ODA File Converter 插件

0. 背景 需要打开.dwg 格式的文件&#xff0c;AutoCAD 又要收费&#xff0c;所以使用法国的 FreeCAD&#xff0c; 安装完成打开.dwg 格式的文件时&#xff0c;出现问题如图所示&#xff1a; 1.下载插件 插件 ODA File Converter 网址 windows 插件在最下面&#xff1a; 2…

Android 性能优化(二):LeakCanary【用于分析代码是否存在内存泄漏】程序无响应

目录 1&#xff09;内存相关的五种常见问题 2&#xff09;内存溢出和内存泄漏 3&#xff09;LeakCanary是什么? 4&#xff09;LeakCanary如何使用&#xff0c;如何分析&#xff1f; 5&#xff09;LeakCanary监测的内容 提问&#xff1a;程序有时候很卡&#xff0c;经常会出现…

再论pg归档日志的设置

用过ORACLE的朋友&#xff0c;第一次设置 PG的归档参数&#xff0c;如下&#xff1a; 。。。 wal_level replica archive_mode on archive_command test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f 。。。 对于归档&#xff0c;还要用…

ConvGRU原理与开源代码

ConvGRU 1. 算法简介与应用场景2. 算法原理2.1 GRU基础2.2 ConvGRU原理2.2.1 ConvGRU的结构2.2.2 卷积操作的优点 2.3 GRU与ConvGRU的对比分析2.4 ConvGRU的应用 3. PyTorch代码 仅需要网络源码的可以直接跳到末尾即可 需要ConvLSTM的可以参考我的另外一篇博客&#xff1a;小白…

Halcon Blob分析

斑点分析的思路&#xff1a;在图像中&#xff0c;相关对象的像素可以通过其灰度值来识别。例如下图的组织颗粒。这些颗粒是凉的&#xff0c;而液体是暗的&#xff0c;通过选择明亮像素(阈值)&#xff0c;可以很容易地检测到颗粒。在需要应用中&#xff0c;这种简单的暗像素和亮…

成像光学:LCD的工作原理与结构图解

一、主流显示面板技术&#xff1a;LCD&#xff0c;OLED&#xff0c;MicroLED 二、主流显示屏的发展趋势 三、LCD堆叠结构&#xff08;以比较流行的TFT-LCD为例&#xff09; 沿光路方向介绍&#xff1a;背光&#xff0c;下偏光片&#xff08;polarizer&#xff09;&#xff0c;…

python实现图像分割算法2

python实现随机步行算法 随机步行算法数学模型Python 实现详细解释优缺点应用领域随机步行算法是一种常用于图像分割和图像分析的算法。它通过模拟随机游走来确定图像中每个像素的标签或类别。随机步行算法特别适合用于解决有种子标记的图像分割问题,其中用户提供一些初始标记…

【Python】基础语法(上)

本篇文章讲解以下知识&#xff1a; &#xff08;1&#xff09;初始编码 &#xff08;2&#xff09;输出 &#xff08;3&#xff09;初识数据类型 一&#xff1a;初识编码 在计算机中所有的数据本质上都是以0和1的组合来存储。 比如&#xff1a;在一个文件中有以下内容&am…

力扣SQL50 上级经理已离职的公司员工 一题双解

Problem: 1978. 上级经理已离职的公司员工 Code -- 方法 1 -- select e1.employee_id -- from employees e1 -- left join employees e2 -- on e1.manager_id e2.employee_id -- where e1.salary < 30000 -- and e1.manager_id is not null -- and e2.employee_id is…

SpringBoot 整合 Redis 实现验证码登录功能

一、整合Redis 在pom.xml中添加Redis相关依赖&#xff1b; <!--Spring Data Redis依赖配置--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>…

103.qt qml-最全Table新增下拉复制功能

在上篇文章102.qt qml-最全Table交互之多列固定、行列拖拽、自定义委托、标题交互使用教程_qt 表格控件 拖动列-CSDN博客 我们实现了大部分功能,所以本章实现下拉复制功能。 demo截图如下所示: 支持跨界复制,如果下拉的位置大于Table则会动画向下移动,具体可以参考视频链接…

颠覆未来计算!CRAM技术摒弃冯·诺依曼模型,20年研究终迎突破

未来科技&#xff1a;AI计算需求激增&#xff0c;数据中心耗电量堪比派对狂饮&#xff01;明尼苏达大学研究团队或携革命性设备&#xff0c;以惊人能效解决AI能耗难题&#xff01; 研究人员设计了一种新型的"计算随机存取存储器"&#xff08;CRAM&#xff09;原型芯…

查看路由表 netstat -r

“Kernel IP routing table” 是Linux系统中用于展示和配置IP路由的表。它告诉操作系统如何将数据包从一个网络接口发送到另一个网络或主机。下面是对您给出的路由表条目的解释&#xff1a; Destination&#xff1a;目的地地址&#xff0c;可以是具体的IP地址&#xff0c;也可…