用 API 实现 AI 视频摘要:动手制作属于你的 AI 视频小助手

news2024/10/2 8:38:32

AI 视频摘要想必你一定不陌生,在各大视频平台,比如 B 站,评论区的 AI 视频小助手就如雨后春笋般遍地都是。

今天,让我们来填了这“护城河”,站到墙上看一看它的全貌。

简而言之,AI 视频摘要的工作流程如下:

  1. 视频 → 音频(MP3)
  2. 音频 → 字幕(带时间戳)
  3. 字幕 → AI 摘要

是不是很简单?你可能会好奇,AI 视频小助手真的“看”视频了吗?其实大概率是没有的,它只是分析了字幕和对应的时间戳。哦,对了,时间戳对应字幕发生的时间。

AI 视频摘要看似有些夸大其词,但实际上却名副其实。因为对于绝大多数信息量丰富,需要总结的视频来说,音频蕴含的信息足以贯穿全文(实际上,它更像是“AI 音频摘要”,不过“AI 视频摘要”显然更有噱头)。

这篇文章将带你一步步的实现 AI 摘要功能。同时,也为生成式人工智能导论课程中 HW9: Quick Summary of Lecture Video 提供中文引导。

建议搭配代码文件逐块运行的同时理解文章。
注意,这里没有任何模型会被训练。

前置:0. 阿里大模型API获取步骤
代码文件下载: 交互完整版 | 非交互精简版 | 🎡AI Summarizer 脚本

文章目录

  • 第 0 部分 - 运行脚本(推荐,但可以跳过)
  • 第 1 部分 - 准备工作
    • 视频转音频
      • Linux:
      • MacOS
      • Windows:
    • 准备音频文件
    • 安装必要的库
    • 导入库
    • 加载数据
      • 直接加载 .mp3 文件
  • 第 2 部分 - 自动语音识别 (ASR)
    • 定义语音识别函数
    • 设置参数
    • 运行语音识别
    • 检查结果
  • 第 3 部分 - 处理自动语音识别的结果
    • 提取字幕文本
    • 拆分文本
    • 执行文本处理
  • 第 4 部分 - 文本摘要
    • 设置 OpenAI API
    • 设置参数
    • 定义摘要函数
    • 这里演示两种摘要方式
      • 方法一:拆分为多段进行摘要(Multi-Stage Summarization)- MapReduce
      • 方法二:精炼方法(the method of Refinement) - Refine
  • 总结与展望
    • 对深度学习一窍不通也可以做出 AI 应用吗?
    • 可能的疑问
  • 参考链接
  • 🎡探索

第 0 部分 - 运行脚本(推荐,但可以跳过)

如果你尚未配置运行环境,请参照 README。

建议运行 🎡AI Summarizer 脚本获得直观的体验。

Pipeline

  1. 克隆整个仓库,确保相关配置文件和样例视频被下载:

    git clone https://github.com/Hoper-J/AI-Guide-and-Demos-zh_CN.git
    cd AI-Guide-and-Demos-zh_CN/CodePlayground
    
  2. 命令行运行:

    python summarizer.py examples/summarizer.mp4
    

    你会被要求输入你的 API 密钥,随后你将获得以下三个文件:

    • output/summarizer.mp3 - 音频文件
    • output/summarizer.srt - 字幕文件
    • output/summarizer.summary.txt - 摘要文件

    你可以检查这些文件,并在后续步骤中使用它们(如果你想的话)。

第 1 部分 - 准备工作

之前装过的模块可以跳过。

视频转音频

你可以自己准备一个视频文件进行实验,也可以跳过这一部分,因为我们已经为你提供了一个音频文件。假设你准备的是 .mp4 格式的视频文件,可以使用 ffmpeg 轻松将其转换为 .mp3 格式。

首先,我们需要安装 ffmpeg。选择你的操作系统进行:

Linux:

sudo apt-get install ffmpeg

MacOS

brew install ffmpeg

Windows:

  1. 前往 FFmpeg 官网 下载对应的安装包。
  2. 按照教程安装并将 ffmpeg 路径添加到环境变量。

安装完成后,使用以下命令将视频文件(input_video.mp4)转为 MP3 音频(output_audio.mp3),你需要修改对应文件名:

ffmpeg -i input_video.mp4 -q:a 0 -map a output_audio.mp3

参数解释:

  • -i input_video.mp4:指定输入的视频文件。
  • -q:a 0:设置音频质量,0 为最高音频质量。
  • -map a:只提取音频部分。
  • output_audio.mp3:指定输出的音频文件名。

准备音频文件

为了对齐课程作业,我们使用李琳山教授《信号与人生 (2023)》演讲的一个片段(从 1:43:24 到 2:00:49),已经上传了对应的 MP3 文件。

如果你感兴趣,也可以通过以下链接查看原始视频:

  • 视频链接
    • Bilibili
    • YouTube

安装必要的库

在开始之前,需要安装一些必要的 Python 库:

pip install srt==3.5.3
pip install datasets==2.20.0
pip install DateTime==5.5
pip install opencv-python-headless==4.10.0.84
pip install openpyxl==3.1.4
pip install openai==1.35.3
pip install git+https://github.com/openai/whisper.git
pip install numpy==1.25.2
pip install soundfile==0.12.1
pip install ipywidgets==8.0.0
pip install librosa==0.10.2.post1

导入库

# 标准库
import os
import time
import re
import pathlib
import textwrap
import datetime

# 第三方库
import numpy as np
import srt
import soundfile as sf
from tqdm import tqdm

# 项目相关库
import whisper
from datasets import load_dataset
from openai import OpenAI

加载数据

对应的数据文件来自于:kuanhuggingface/NTU-GenAI-2024-HW9。

Parquet 文件也已经上传至 data 文件夹,无需再下载。

# 加载本地 Parquet 格式的数据集
dataset = load_dataset('parquet', data_files={'test': './data/13/test-00000-of-00001.parquet'})

# 准备音频
input_audio = dataset["test"]["audio"][0]
input_audio_name = input_audio["path"]
input_audio_array = input_audio["array"].astype(np.float32)
sampling_rate = input_audio["sampling_rate"]

在这个过程中,input_audio_array 包含了音频数据,input_audio_name 是音频文件名,sampling_rate 是采样率。

直接加载 .mp3 文件

那如果我们想使用 .mp3 文件而不是这个从来没见过的文件格式的话,应该怎么办呢?

Demos/data/13/audio.mp3 的文件为例:

import librosa

# 指定 MP3 文件路径
mp3_file_path = './data/13/audio.mp3'

input_audio_name = os.path.basename(mp3_file_path)

# 加载音频文件,指定采样率为 16000
input_audio_array, sampling_rate = librosa.load(mp3_file_path, sr=16000)

# 打印音频数据的采样率和形状,确保加载成功
print(f"采样率: {sampling_rate}")
print(f"音频数据形状: {input_audio_array.shape}")

print(f"现在我们将转录音频: ({input_audio_name})。")

具体变量和参数解释:

  • input_audio_array:音频数据的 NumPy 数组表示。
  • sampling_rate:音频的采样率(Hz)。
  • input_audio_name:音频文件名,仅保留文件名,不包含路径。
  • 注意:我们使用 sr=16000,将音频采样率转换为 Whisper 模型要求的 16000 Hz,确保模型能够正确处理音频数据。

这样,我们就完成了音频文件的导入。你可以进一步替换成你自己的 .mp3 文件。

第 2 部分 - 自动语音识别 (ASR)

Automatic Speech Recognition
image-20240926155151471

Whisper 是 OpenAI 开源的通用语音识别模型,支持多种语言的语音识别任务,并且能够为音频文件生成带有时间戳的字幕,在论文中,作者提到:“我们使用了 68 万小时的多语言和多任务监督数据训练了该模型。”

所以你可以相信它的能力,感谢开源精神。

接下来,你可以将某段会议的音频录制作为输入,查看转录后的效果。我们将利用 Whisper 模型来带你完成 ASR。下图是处理的样例过程:

image-20240926155512340

定义语音识别函数

def speech_recognition(model_name, input_audio, output_subtitle_path, decode_options, cache_dir="./"):
    # 加载模型
    model = whisper.load_model(name=model_name, download_root=cache_dir)

    # 转录音频
    transcription = model.transcribe(
        audio=input_audio,
        language=decode_options["language"],
        verbose=False,
        initial_prompt=decode_options["initial_prompt"],
        temperature=decode_options["temperature"]
    )

    # 处理转录结果,生成字幕文件
    subtitles = []
    for i, segment in enumerate(transcription["segments"]):
        start_time = datetime.timedelta(seconds=segment["start"])
        end_time = datetime.timedelta(seconds=segment["end"])
        text = segment["text"]
        subtitles.append(srt.Subtitle(index=i, start=start_time, end=end_time, content=text))

    srt_content = srt.compose(subtitles)

    # 保存字幕文件
    with open(output_subtitle_path, "w", encoding="utf-8") as file:
        file.write(srt_content)

    print(f"字幕已保存到 {output_subtitle_path}")

设置参数

注意,这里设置的参数都是 Whisper 相关的,与后续的 AI 摘要不同。

你可以通过 model_name 设置不同的模型,使用标识指定。通过官方仓库所提供的数据,我们可以看到不同模型需要的显存大小:

SizeParametersEnglish-only modelMultilingual modelRequired VRAMRelative speed
tiny39 Mtiny.entiny~1 GB~32x
base74 Mbase.enbase~1 GB~16x
small244 Msmall.ensmall~2 GB~6x
medium769 Mmedium.enmedium~5 GB~2x
large1550 MN/Alarge~10 GB1x

解释:

  • Size (大小):表示模型的尺寸,不同大小的模型训练时使用的数据量不同,因此性能和精度也不同。较大的模型通常会有更高的精度。Medium 是个不错的选择,tinybase 效果一般,用于学习的话也可以。
  • Parameters (参数量):模型的参数数量,表示模型的复杂度。参数越多,模型的性能通常越好,但也会占用更多的计算资源。
  • English-only model (仅限英文模型):模型的标识名称,只用于处理英文音频转录,适用于仅需要处理英文语音的场景。
  • Multilingual model (多语言模型):模型的标识名称,用于在代码中加载相应的模型,对应于接下来的 model_name 参数。
  • Required VRAM (所需显存):指运行该模型时所需的显存大小。如果你对参数和显存的对应关系感兴趣,可以阅读之前的文章:《07. 探究模型参数与显存的关系以及不同精度造成的影响.md》。
  • Relative speed (相对速度):相对速度表示模型处理语音转录任务的效率。数字越大,模型处理速度越快,与模型的参数量成反比。
# 模型名称,可选 'tiny', 'base', 'small', 'medium', 'large-v3'
model_name = 'medium'

# 语言
language = 'zh'  # 选择语音识别的目标语言,如 'zh' 表示中文

# 初始 prompt,可选
initial_prompt = '请用中文'  # 如果需要,可以为 Whisper 模型设置初始 prompt 语句

# 采样温度,控制模型的输出多样性
temperature = 0.0  # 0 表示最确定性的输出,范围为 0-1

# 输出文件后缀
suffix = '信号与人生'

# 字幕文件路径
output_subtitle_path = f"./output-{suffix}.srt"

# 模型缓存目录
cache_dir = './'

initial_prompt:这个参数用于设置 Whisper 模型转录任务的初始提示,为模型提供上下文信息。对于不连续的对话或者需要保持一致性的转录任务,initial_prompt 可以帮助模型更好地理解和预测后续的语音内容。

例如,如果你有一个分段的长视频,并且每段视频之间有相关的上下文信息,你可以使用 initial_prompt 来告知模型当前的语境。

运行语音识别

# 构建解码选项
decode_options = {
    "language": language,
    "initial_prompt": initial_prompt,
    "temperature": temperature
}

# 运行 ASR
speech_recognition(
    model_name=model_name,
    input_audio=input_audio_array,
    output_subtitle_path=output_subtitle_path,
    decode_options=decode_options,
    cache_dir=cache_dir
)

运行成功后,Whisper 模型会生成带有时间戳的字幕文件(.srt 格式),保存在你指定的路径中。

image-20240924133743569

检查结果

# 读取并打印字幕内容
with open(output_subtitle_path, 'r', encoding='utf-8') as file:
    content = file.read()
print(content)

你将看到生成的字幕内容,包括时间戳和对应的文字。

image-20240924133837165

JUST DO IT

第 3 部分 - 处理自动语音识别的结果

在这一部分,我们将处理通过 Whisper 模型生成的字幕文件(SRT 文件),提取其中的纯文本内容,并对文本进行拆分以便后续的处理。

提取字幕文本

SRT 文件包含了字幕的索引、时间戳(字幕显示的起始和结束时间)以及字幕文本内容。我们这里做的是简单处理,只保留了每个字幕条目的文本部分,去除了时间戳和索引信息。

def extract_and_save_text(srt_filename, output_filename):
    # 读取 SRT 文件
    with open(srt_filename, 'r', encoding='utf-8') as file:
        content = file.read()

    # 去除时间戳和索引
    pure_text = re.sub(r'\d+\n\d{2}:\d{2}:\d{2},\d{3} --> \d{2}:\d{2}:\d{2},\d{3}\n', '', content)
    pure_text = re.sub(r'\n\n+', '\n', pure_text)

    # 保存纯文本
    with open(output_filename, 'w', encoding='utf-8') as output_file:
        output_file.write(pure_text)

    print(f'提取的文本已保存到 {output_filename}')

    return pure_text

拆分文本

这里我们使用了 textwrap.wrap 函数,将纯文本按 max_length 个字符的长度进行划分。

def chunk_text(text, max_length):
    return textwrap.wrap(text, max_length)

每个生成的文本块都会包含最多 512 个字符(包括空格和标点符号),这是一种简单的分割方法。

执行文本处理

调用之前的函数进行文本提取和拆分。

# 文本块长度
chunk_length = 512

# 提取文本并拆分
pure_text = extract_and_save_text(
    srt_filename=output_subtitle_path,
    output_filename=f"./output-{suffix}.txt",
)

chunks = chunk_text(text=pure_text, max_length=chunk_length)

第 4 部分 - 文本摘要

设置 OpenAI API

首先,需要设置 OpenAI API 密钥,这里演示使用的是阿里云的大模型 API,你可以通过《00. 阿里大模型API获取步骤》获取 API 密钥。

如果需要使用其他平台,请参考对应的开发文档后对应修改 base_url

# 设置 OpenAI API 密钥
openai_api_key = 'your-api-key-here'  # 请将 'your-api-key-here' 替换为你的实际 API 密钥

# 构建 OpenAI 客户端
client = OpenAI(
    api_key=openai_api_key,
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", 
)

设置参数

这里就是 AI 摘要相关的参数了。

# 模型名称
model_name = 'qwen-turbo'

# 控制响应的随机性
temperature = 0.0

# 控制多样性
top_p = 1.0

# 最大生成标记数
max_tokens = 512

默认使用 qwen-turbo,其他模型可以参阅模型广场 – 阿里云百炼,点击对应模型的查看详情

image-20240924091151684

你可以在界面左上角看到对应的英文名称,复制它,然后替换 model_name

image-20240924091414350

你可以随意更换为你想要的模型,不过可能要先申请使用(通过大概要几个小时,会有短信提示)。

定义摘要函数

def summarization(client, summarization_prompt, model_name="qwen-turbo", temperature=0.0, top_p=1.0, max_tokens=512):
    response = client.chat.completions.create(
        messages=[{"role": "user", "content": summarization_prompt}],
        model=model_name,
        temperature=temperature,
        top_p=top_p,
        max_tokens=max_tokens
    )
    return response.choices[0].message.content

这里演示两种摘要方式

分别对应于 MapReduceRefine,你可以通过接下来的代码来感受二者的区别。

方法一:拆分为多段进行摘要(Multi-Stage Summarization)- MapReduce

image.png

  1. 将长文本分成多个较小的部分,并分别获取每个小段落的摘要

    # 定义摘要提示模板
    summarization_prompt_template = "用 300 个字以内写出这段视频文本的摘要,其中包括要点和所有重要细节:<text>"
    
    # 对每个文本块生成摘要
    paragraph_summaries = []
    for index, chunk in enumerate(chunks):
        print(f"\n========== 正在生成第 {index + 1} 段摘要 ==========\n")
        print(f"原始文本 (第 {index + 1} 段):\n{chunk}\n")
        
        # 构建摘要提示
        summarization_prompt = summarization_prompt_template.replace("<text>", chunk)
        
        # 调用摘要函数
        summary = summarization(
            client=client,
            summarization_prompt=summarization_prompt,
            model_name=model_name,
            temperature=temperature,
            top_p=top_p,
            max_tokens=max_tokens
        )
        
        # 打印生成的摘要
        print(f"生成的摘要 (第 {index + 1} 段):\n{summary}\n")
        
        # 将生成的摘要保存到列表
        paragraph_summaries.append(summary)
    

    image-20240924141506642

  2. 在分别获取每个小段落的摘要后,处理这些摘要以生成最终的摘要。

    # 合并段落摘要
    collected_summaries = "\n".join(paragraph_summaries)
    
    # 定义最终摘要提示模板
    final_summarization_prompt = "在 500 字以内写出以下文字的简洁摘要:<text>"
    final_summarization_prompt = final_summarization_prompt.replace("<text>", collected_summaries)
    
    # 生成最终摘要
    final_summary = summarization(
        client=client,
        summarization_prompt=final_summarization_prompt,
        model_name=model_name,
        temperature=temperature,
        top_p=top_p,
        max_tokens=max_tokens
    )
    
    print(final_summary)
    

    image-20240924141608944

方法二:精炼方法(the method of Refinement) - Refine

Refinement 就是把每次的文本和之前的摘要结合起来丢给大模型,类似于迭代:

image-20240924092753352

步骤(Pipeline)如下:

  • 第1步:从一小部分数据开始,运行prompt生成初始输出。
  • 第2步:对后续每个文档,将前一个输出与新文档结合输入。
  • 第3步:LLM 根据新文档中的信息精炼输出。
  • 第4步:此过程持续迭代,直到处理完所有文档。

对应代码:

# 定义初始摘要提示模板
summarization_prompt_template = "用 300 个字以内写出这段视频文本的摘要,其中包括要点和所有重要细节:<text>"

# 定义精炼摘要提示模板
summarization_prompt_refinement_template = "请在 500 字以内,结合原先的摘要和新的内容,提供简洁的摘要:<text>"

# 初始化保存摘要的列表
refined_summaries = []

# 对文本块逐步进行精炼摘要,并打印中间过程
for index, chunk in enumerate(chunks):
    if index == 0:
        # 第一步:对第一段文本生成初始摘要
        print(f"\n========== 正在生成第 {index + 1} 段的初始摘要 ==========\n")
        print(f"原始文本 (第 {index + 1} 段):\n{chunk}\n")
        
        # 构建初始摘要提示
        summarization_prompt = summarization_prompt_template.replace("<text>", chunk)
        
        # 调用摘要函数生成第一个摘要
        first_summary = summarization(
            client=client,
            summarization_prompt=summarization_prompt,
            model_name=model_name,
            temperature=temperature,
            top_p=top_p,
            max_tokens=max_tokens
        )
        
        # 打印生成的初始摘要
        print(f"生成的摘要 (第 {index + 1} 段):\n{first_summary}\n")
        
        # 保存生成的摘要
        refined_summaries.append(first_summary)

    else:
        # 后续步骤:结合前一个摘要与当前段落进行精炼
        print(f"\n========== 正在生成第 {index + 1} 段的精炼摘要 ==========\n")
        print(f"原始文本 (第 {index + 1} 段):\n{chunk}\n")
        
        # 构建精炼摘要的输入文本,将前一个摘要与当前段落内容结合
        chunk_with_previous_summary = f"前 {index} 段的摘要: {refined_summaries[-1]}\n第 {index + 1} 段的内容: {chunk}"
        
        # 构建精炼摘要提示
        summarization_prompt = summarization_prompt_refinement_template.replace("<text>", chunk_with_previous_summary)
        
        # 调用摘要函数生成精炼摘要
        refined_summary = summarization(
            client=client,
            summarization_prompt=summarization_prompt,
            model_name=model_name,
            temperature=temperature,
            top_p=top_p,
            max_tokens=max_tokens
        )
        
        # 打印生成的精炼摘要
        print(f"生成的摘要 (第 {index + 1} 段):\n{refined_summary}\n")
        
        # 保存生成的精炼摘要
        refined_summaries.append(refined_summary)

# 最终的精炼摘要结果就是 refined_summaries 列表的最后一个元素
final_refined_summary = refined_summaries[-1]

print("\n========== 最终精炼摘要结果 ==========\n")
print(final_refined_summary)

image-20240924142344048

总结与展望

对深度学习一窍不通也可以做出 AI 应用吗?

当然可以!或许你曾经看到 AI 视频小助手的时候会觉得:“哇,肯定很多技术细节在里面,学习曲线一定很陡峭。”但实际上,在真正用到模型的地方,你只需要加载预训练模型和调用 API 就可以了,完全不需要自己训练模型:

  • 音频提取:从视频中提取音频,这一步可以通过各种工具实现,比如 ffmpeg
  • 语音识别:使用开源的 ASR(自动语音识别)模型,如 OpenAI 的 Whisper,将音频转换为文字。
  • 文本摘要:借助大语言模型(LLM)对文本进行摘要,可以直接调用 OpenAI API 或其他大模型 API 实现。

因此,所有这些看似复杂的任务其实都是你可以轻松做到的。AI 应用并没有想象中那么神秘,只要你了解工具的使用方法,就可以轻松去实现你的想法。

🤔再打开一下思路:现在,大多数的视频都有字幕或者 AI 字幕,这为我们提供了一个捷径。通过直接下载这些字幕,你的 AI 应用就能跳过音频转录等步骤,直接进入它最擅长的环节:生成摘要。

So,让我们再次回到最初的疑问:AI 视频小助手真的“看”视频了吗?

:不仅没看,还大概率没听。如果某个线上的 AI 小助手在可以正常下载到音频的情况下却不能做总结,这就表示:它读的是字幕。

可能的疑问

摘要没有提供时间段,还是做不到和评论区的小助手一样的事情。

确实,目前我们生成的摘要并没有包含具体的时间戳信息,不像一些评论区的 AI 小助手那样,为每个摘要段落提供对应的视频时间点。这是因为我们在 extract_and_save_text() 处理的时候就把时间戳信息拿掉了,为什么这样做呢?

:提供一个简单的处理概览,以及对齐作业 😃

如何解决这个问题?

:文本处理时保留时间戳 + 修改 prompt 模版,增加 <start_time> - <end_time> 占位符。

这篇文章更多的作为一个入门文章,希望能帮你揭开面纱的一角。

参考链接

  • 生成式人工智能导论
  • HW9 PDF 当前文章中的有趣的图片大多来自于此。
  • Whisper - Paper
  • Whisper - Github

🎡探索

我制作了 Toy 版本的🎡AI Summarizer 脚本,你可以简单的使用下面的命令获取摘要以及对应的中间文件:

python summarizer.py examples/summarizer.mp4

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

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

相关文章

使用socket编程来实现一个简单的C/S模型(TCP协议)

前置 所使用到的函数查看本专栏中&#xff1a;socket的概念和常用函数介绍 1.C/S模型 - TCP 下图是基于TCP协议的客户端/服务器程序的一般流程&#xff1a; 服务器调用socket()、bind()、listen()完成初始化后&#xff0c;调用accept()阻塞等待&#xff0c;处于监听端口的状…

第7课-C/C++ 高效内存管理

1. C/C 内存分布 在 C 和 C 中&#xff0c;内存可以分为多个区域&#xff0c;包括栈、堆、数据段、代码段等。这些区域分别用来存储不同类型的数据。通过以下示例代码&#xff0c;我们可以直观地理解这些区域的作用&#xff1a; int globalVar 1; // 全局变量 sta…

停止模式下USART为什么可以唤醒MCU?

在MCU的停止模式下&#xff0c;USART之类的外设时钟是关闭的&#xff0c;但是USART章节有描述到在停止模式下可以用USART来对MCU进行唤醒&#xff1a; 大家是否会好奇在外设的时钟被关闭的情况下&#xff0c;USART怎么能通过接收中断或者唤醒事件对MCU进行唤醒的呢&#xff1…

2024双十一有什么值得买?分享五款优质好物提高幸福感!

双十一购物节即将到来&#xff0c;这是一年中各平台打折力度最大的时期。然而&#xff0c;在众多品牌和款式中&#xff0c;我们往往难以做出选择。今天&#xff0c;我将为大家介绍一些在双十一期间值得入手的高品质商品&#xff0c;让我们一起寻找心仪之选&#xff01; 1、水陆…

【微服务】初识

基础概念 集群 集群是将一个系统完整的部署到多个服务器&#xff0c;每个服务器提供系统的所有服务&#xff0c;多个服务器可以通过负载均衡完成任务&#xff0c;每个服务器都可以称为集群的节点。 分布式 分布式是将一个系统拆分为多个子系统&#xff0c;多个子系统部署在…

入门案例解析

parent aliyun上下载的却没有parent——但是在下面导入了 这里进行了继承——且继承得里面进行了依赖管理&#xff08;插件管理也是如此&#xff09; 不同版本的SpringBoot的依赖版本会有一些不同 starter 这就可以让我使用某个技术开发就可以使用某个技术的starter …

PDSCH(物理下行共享信道)简介

文章目录 PDSCH&#xff08;物理下行共享信道&#xff09;简介1. Transport block CRC attachment2. LDPC base graph selection3. Code block segmentation And Code Block CRC Attachment4. Channel Coding5. Rate Matching6. Code Block Concatenation7. Scrambling8. Modul…

DBC差异比较工具DBCCompare_原理介绍(四)

DBC比对工具UI图片 DBC比对工具&#xff1a;功能详解与源码分析 在现代汽车开发和诊断过程中&#xff0c;DBC&#xff08;Database Container&#xff09;文件扮演着至关重要的角色。它们详细描述了CAN&#xff08;Controller Area Network&#xff09;网络中各消息和信号的详…

JavaScript中的(this)指向问题(如何正确判断this,箭头函数的this是什么)

&#x1f43e;如何正确判断this &#x1f449;我们先运用下面的代码&#xff0c;模拟我们日常生活中常见的三个开发场景&#xff0c;并针对每个场景我们来一 一介绍this的指向 function fun1() {console.log(this.a)}var a 1//场景1fun1()const obj {a: 2,fun1: fun1}//场景…

Arduino UNO R3自学笔记17 之 Arduino为啥要用中断?

注意&#xff1a;学习和写作过程中&#xff0c;部分资料搜集于互联网&#xff0c;如有侵权请联系删除。 前言&#xff1a;学习Arduino中断的概念及其功能。 1.什么是中断&#xff1f; 单片机在执行程序时&#xff0c;发生一些其它紧急的事情&#xff0c;单片机将立即暂停当前…

RD-Agent Windows安装教程

RD-Agent Windows安装教程 QuantML QuantML 2024年09月23日 18:30 Content RD-Agent 是微软亚洲研究院推出的一款自动化研究与开发工具&#xff0c;能够通过LLMs自动构建因子和策略&#xff0c;相关介绍见我们之前的文章&#xff1a;RD-Agent &#xff1a;自动化Quant工厂 然…

Redis: Sentinel哨兵监控架构及环境搭建

概述 在主从模式下&#xff0c;我们通过从节点只读模式提高了系统的并发能力并发不断增加&#xff0c;只需要扩展从节点即可&#xff0c;只要主从服务器之间&#xff0c;网络连接正常主服务器就会将写入自己的数据同步更新给从服务器&#xff0c;从而保证主从服务器的数据相同…

国外电商系统开发-用户第一次需求反馈

一、用户反馈 因用户不懂系统开发&#xff0c;不知道需求应该怎么整理&#xff0c;用户只能从页面端说。 1、首页 a、太花里胡哨啦&#xff0c;不是一目了然&#xff1b; b、主次感不是很强&#xff1b; 2、分类 a、太复杂&#xff0c;前期产品不多 3、详情 a、太多了广…

极端天气道路目标检测数据集 3400张 带标注 VOC YOLO 6类

分类名: (图片张数&#xff0c;标注个数) car: (3210&#xff0c; 13654) truck: (1168&#xff0c;1629) per son: (1517&#xff0c;4359) bicyc le: (334, 589) bus: (381&#xff0c; 439) motorcycle: (164, 214) 总数: (3404, 20884) 总类(nc): 6类 极端天气道路目标检测…

RAG(Retrieval Augmented Generation)及衍生框架:CRAG、Self-RAG与HyDe的深入探讨

近年来&#xff0c;随着大型语言模型&#xff08;LLMs&#xff09;的迅猛发展&#xff0c;我们在寻求更精确、更可靠的语言生成能力上取得了显著进展。其中&#xff0c;检索增强生成&#xff08;Retrieval-Augmented Generation&#xff09;作为一种创新方法&#xff0c;极大地…

<<机器学习实战>>10-11节笔记:生成器与线性回归手动实现

10生成器与python实现 如果是曲线规律的数据集&#xff0c;则需要把模型变复杂。如果是噪音较大&#xff0c;则需要做特征工程。 随机种子的知识点补充&#xff1a; 根据不同库中的随机过程&#xff0c;需要用对应的随机种子&#xff1a; 比如 llist(range(5)) random.shuf…

Linux 实用工具Axel安装及使用教程(支持多线程下载)

一、Axel 简介 Axel 是一个轻量级的命令行下载加速器&#xff0c;旨在提高文件下载速度。 多线程下载: Axel 可以同时使用多个连接来下载文件&#xff0c;从而加快下载速度。断点续传: 支持中断后继续下载&#xff0c;避免重新开始下载整个文件。轻量级: 资源占用少&#xff0c…

G502 鼠标自定义(配合 karabiner)

朋友送了我一个 G502 多功能鼠标&#xff0c;除了鼠标正常的左键、右键和滑轮外&#xff0c;额外提供了 6 个按键&#xff0c;并且滑轮可以向左、向右、向下按下&#xff0c;共计 9 个自定义的按键。 虽然是 karabiner 的老用户&#xff0c;但一直在使用 TrackPad&#xff0c;所…

SpringBoot上传图片实现本地存储以及实现直接上传阿里云OSS

一、本地上传 概念&#xff1a;将前端上传的文件保存到自己的电脑 作用&#xff1a;前端上传的文件到后端&#xff0c;后端存储的是一个临时文件&#xff0c;方法执行完毕会消失&#xff0c;把临时文件存储到本地硬盘中。 1、导入文件上传的依赖 <dependency><grou…

C++ | Leetcode C++题解之第451题根据字符出现频率排序

题目&#xff1a; 题解&#xff1a; class Solution { public:string frequencySort(string s) {unordered_map<char, int> mp;int maxFreq 0;int length s.size();for (auto &ch : s) {maxFreq max(maxFreq, mp[ch]);}vector<string> buckets(maxFreq 1)…