在huggingface上制作小demo

news2025/4/16 17:06:32

在huggingface上制作小demo

今天好兄弟让我帮他搞一个模型,他有小样本的化学数据,想让我根据这些数据训练一个小模型,他想用这个模型预测一些值
最终我简单训练了一个小模型,起初想把这个模型和GUI界面打包成exe发给他,但是发现打包后3.9GB,太大了吧!!!后来我又找了别的方案,即将训练好的模型以及相关代码、环境配置文件上传到huggingface上,通过hf的界面端直接使用这个模型,接下来我回顾一下整个流程

1.训练模型并写一个简单的GUI

训练数据

模型输入值:substance、N、C、C/N、K、Cellulose、Hemicellulose、Lignin
模型输出值:Alkaliniy400、Alkalinity600

训练代码train.py
由于样本较小,为了减小误差,这里采用5折交叉验证

import pandas as pd
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader
from sklearn.model_selection import KFold
from sklearn.preprocessing import StandardScaler, OneHotEncoder
import joblib


# 定义神经网络模型
class AlkalinityNet(nn.Module):
    def __init__(self, input_dim):
        super(AlkalinityNet, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(input_dim, 64),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(64, 32),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(32, 2)  # 输出两个值:400℃ 和 600℃ 碱度
        )

    def forward(self, x):
        return self.model(x)


def train_model(model, train_loader, criterion, optimizer, device):
    model.train()
    running_loss = 0.0
    for X_batch, y_batch in train_loader:
        X_batch = X_batch.to(device)
        y_batch = y_batch.to(device)
        optimizer.zero_grad()
        outputs = model(X_batch)
        loss = criterion(outputs, y_batch)
        loss.backward()
        optimizer.step()
        running_loss += loss.item() * X_batch.size(0)
    return running_loss


def evaluate_model(model, val_loader, criterion, device):
    model.eval()
    running_loss = 0.0
    with torch.no_grad():
        for X_batch, y_batch in val_loader:
            X_batch = X_batch.to(device)
            y_batch = y_batch.to(device)
            outputs = model(X_batch)
            loss = criterion(outputs, y_batch)
            running_loss += loss.item() * X_batch.size(0)
    return running_loss


def main():
    # 读取 Excel 数据
    data = pd.read_excel('~/PycharmProjects/Alkalinity/datasets/data.xlsx')

    # 假设第一列为物质名称,列名为 "Substance"
    # 数值特征
    num_features = ["N", "C", "C/N", "K", "Cellulose", "Hemicellulose", "Lignin"]
    targets = ["Alkalinity400", "Alkalinity600"]

    # 提取物质类别和数值特征
    substances = data["Substance"].values.reshape(-1, 1)
    X_num = data[num_features].values
    y = data[targets].values.astype(np.float32)

    # 对物质类别使用 OneHotEncoder 编码
    encoder = OneHotEncoder(sparse_output=False, handle_unknown='ignore')
    X_cat = encoder.fit_transform(substances)
    joblib.dump(encoder, 'encoder.pkl')

    # 对数值特征进行标准化
    scaler_X = StandardScaler()
    X_num_scaled = scaler_X.fit_transform(X_num)
    joblib.dump(scaler_X, 'scaler_X.pkl')

    # 拼接类别特征和数值特征
    X_combined = np.hstack([X_cat, X_num_scaled]).astype(np.float32)

    # 对目标值进行标准化
    scaler_y = StandardScaler()
    y_scaled = scaler_y.fit_transform(y)
    joblib.dump(scaler_y, 'scaler_y.pkl')

    # 转换为 PyTorch tensor
    X_tensor = torch.from_numpy(X_combined)
    y_tensor = torch.from_numpy(y_scaled)

    # 设置设备
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    # 交叉验证设置
    kf = KFold(n_splits=5, shuffle=True, random_state=42)
    num_epochs = 100
    batch_size = 8
    criterion = nn.MSELoss()
    fold_losses = []

    print("开始 5 折交叉验证...")
    for fold, (train_index, val_index) in enumerate(kf.split(X_tensor), 1):
        X_train, X_val = X_tensor[train_index], X_tensor[val_index]
        y_train, y_val = y_tensor[train_index], y_tensor[val_index]

        train_dataset = TensorDataset(X_train, y_train)
        val_dataset = TensorDataset(X_val, y_val)

        train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
        val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)

        model = AlkalinityNet(input_dim=X_tensor.shape[1]).to(device)
        optimizer = optim.Adam(model.parameters(), lr=0.001)

        for epoch in range(num_epochs):
            train_loss = train_model(model, train_loader, criterion, optimizer, device)
            val_loss = evaluate_model(model, val_loader, criterion, device)
            # 这里可以打印每折每轮的损失,也可以选择每隔一定轮数打印一次
            # print(f"Fold {fold}, Epoch {epoch+1}/{num_epochs}, Train Loss: {train_loss/len(train_dataset):.4f}, Val Loss: {val_loss/len(val_dataset):.4f}")

        avg_val_loss = val_loss / len(val_dataset)
        print(f"第 {fold} 折验证 Loss: {avg_val_loss:.4f}")
        fold_losses.append(avg_val_loss)

    print("5 折交叉验证平均 Loss:", np.mean(fold_losses))

    # 在全数据集上训练最终模型
    final_dataset = TensorDataset(X_tensor, y_tensor)
    final_loader = DataLoader(final_dataset, batch_size=batch_size, shuffle=True)

    final_model = AlkalinityNet(input_dim=X_tensor.shape[1]).to(device)
    optimizer = optim.Adam(final_model.parameters(), lr=0.001)

    print("开始在全数据集上训练最终模型...")
    for epoch in range(num_epochs):
        train_loss = train_model(final_model, final_loader, criterion, optimizer, device)
        # 可打印训练进度
        # print(f"Epoch {epoch+1}/{num_epochs}, Loss: {train_loss/len(final_dataset):.4f}")

    # 保存最终模型参数
    torch.save(final_model.state_dict(), 'final_model.pth')
    print("最终模型已保存到 final_model.pth")


if __name__ == '__main__':
    main()

推理代码eval.py

import numpy as np
import torch
import torch.nn as nn
import joblib
from sklearn.preprocessing import StandardScaler, OneHotEncoder


# 定义与训练时相同的网络结构
class AlkalinityNet(nn.Module):
    def __init__(self, input_dim):
        super(AlkalinityNet, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(input_dim, 64),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(64, 32),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(32, 2)
        )

    def forward(self, x):
        return self.model(x)


def predict_alkalinity(input_data):
    """
    参数 input_data 为字典,包含以下键值:
       "Substance": 物质名称,例如 "玉米秸秆"
       "N", "C", "C/N", "K", "Cellulose", "Hemicellulose", "Lignin"
    返回预测的 [400℃ 碱度, 600℃ 碱度]
    """
    # 数值特征顺序与训练时一致
    num_features = ["N", "C", "C/N", "K", "Cellulose", "Hemicellulose", "Lignin"]

    # 提取物质名称与数值特征
    substance = np.array([[input_data["Substance"]]])
    X_num = np.array([input_data[feat] for feat in num_features]).reshape(1, -1).astype(np.float32)

    # 加载保存的 OneHotEncoder 和 StandardScaler
    encoder = joblib.load('encoder.pkl')
    scaler_X = joblib.load('scaler_X.pkl')
    scaler_y = joblib.load('scaler_y.pkl')

    X_cat = encoder.transform(substance)
    X_num_scaled = scaler_X.transform(X_num)

    # 拼接类别特征和数值特征
    X_combined = np.hstack([X_cat, X_num_scaled]).astype(np.float32)

    # 转换为 tensor
    X_tensor = torch.from_numpy(X_combined)

    # 加载模型,注意输入维度需与训练时保持一致
    input_dim = X_combined.shape[1]
    model = AlkalinityNet(input_dim=input_dim)
    # 加载模型参数
    model.load_state_dict(torch.load('final_model.pth', map_location=torch.device('cpu'), weights_only=True))
    model.eval()

    with torch.no_grad():
        y_pred_tensor = model(X_tensor)
    y_pred_scaled = y_pred_tensor.numpy()

    # 将预测结果反标准化
    y_pred = scaler_y.inverse_transform(y_pred_scaled)
    return y_pred[0]


if __name__ == '__main__':
    # 示例输入,请根据实际物质成分调整数值
    input_data = {
        "Substance": "鸡粪",
        "N": 1.0,
        "C": 40.0,
        "C/N": 40.0,
        "K": 2.5,
        "Cellulose": 29.0,
        "Hemicellulose": 25.0,
        "Lignin": 12.0
    }
    result = predict_alkalinity(input_data)
    print("预测 400℃ 碱度:", result[0])
    print("预测 600℃ 碱度:", result[1])

本地推理看看

GUI界面代码app.py

import tkinter as tk
from tkinter import messagebox
import tkinter.font as tkFont
import joblib
import numpy as np
import torch
import torch.nn as nn

# 定义与训练时一致的模型结构
class AlkalinityNet(nn.Module):
    def __init__(self, input_dim):
        super(AlkalinityNet, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(input_dim, 64),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(64, 32),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(32, 2)  # 输出 400℃ 和 600℃ 的碱度值
        )

    def forward(self, x):
        return self.model(x)

def predict():
    try:
        # 获取用户输入的各项数值
        substance = entry_substance.get()
        N = float(entry_N.get())
        C = float(entry_C.get())
        C_N = float(entry_CN.get())
        K = float(entry_K.get())
        cellulose = float(entry_cellulose.get())
        hemicellulose = float(entry_hemicellulose.get())
        lignin = float(entry_lignin.get())
    except ValueError:
        messagebox.showerror("输入错误", "请确保所有数值项均正确填写")
        return

    # 构造输入字典
    input_data = {
        "Substance": substance,
        "N": N,
        "C": C,
        "C/N": C_N,
        "K": K,
        "Cellulose": cellulose,
        "Hemicellulose": hemicellulose,
        "Lignin": lignin
    }

    try:
        # 加载保存的预处理器
        encoder = joblib.load('encoder.pkl')
        scaler_X = joblib.load('scaler_X.pkl')
        scaler_y = joblib.load('scaler_y.pkl')
    except Exception as e:
        messagebox.showerror("加载错误", f"加载预处理器失败:{e}")
        return

    # 对物质类别特征进行 one-hot 编码
    substance_arr = np.array([[input_data["Substance"]]])
    X_cat = encoder.transform(substance_arr)

    # 数值特征转换与标准化
    X_num = np.array([[input_data["N"], input_data["C"], input_data["C/N"], input_data["K"],
                       input_data["Cellulose"], input_data["Hemicellulose"], input_data["Lignin"]]], dtype=np.float32)
    X_num_scaled = scaler_X.transform(X_num)

    # 拼接特征
    X_combined = np.hstack([X_cat, X_num_scaled]).astype(np.float32)
    X_tensor = torch.from_numpy(X_combined)

    # 加载模型(注意:模型参数文件和输入预处理器需放在同一目录下)
    input_dim = X_combined.shape[1]
    model = AlkalinityNet(input_dim=input_dim)
    try:
        model.load_state_dict(torch.load('final_model.pth', map_location=torch.device('cpu'), weights_only=True))
    except Exception as e:
        messagebox.showerror("加载模型错误", f"加载模型失败:{e}")
        return
    model.eval()

    with torch.no_grad():
        y_pred_tensor = model(X_tensor)
    y_pred_scaled = y_pred_tensor.numpy()

    # 反标准化得到预测值
    y_pred = scaler_y.inverse_transform(y_pred_scaled)
    pred_400 = y_pred[0, 0]
    pred_600 = y_pred[0, 1]

    # 在界面上显示预测结果
    label_pred_400.config(text=str(pred_400))
    label_pred_600.config(text=str(pred_600))

# 创建 GUI 主窗口
root = tk.Tk()
root.title("碱度预测模型")

# 设置参考尺寸与基础字体大小(可根据需要调整)
REF_WIDTH = 800
REF_HEIGHT = 600
BASE_FONT_SIZE = 18

# 用于存放所有需要动态调整字体的控件
widgets_to_update = []

# 创建一个默认字体对象,初始大小 BASE_FONT_SIZE
default_font = tkFont.Font(family="SimSun", size=BASE_FONT_SIZE)

# 辅助函数:创建标签并加入更新列表(居中显示)
def create_label(text, row, col):
    lbl = tk.Label(root, text=text, font=default_font, anchor="center")
    lbl.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
    widgets_to_update.append(lbl)
    return lbl

# 辅助函数:创建输入框并加入更新列表(内容居中)
def create_entry(row, col):
    ent = tk.Entry(root, font=default_font, justify="center")
    ent.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
    widgets_to_update.append(ent)
    return ent

# 定义行列权重,使控件居中扩展
for i in range(11):
    root.grid_rowconfigure(i, weight=1)
for j in range(2):
    root.grid_columnconfigure(j, weight=1)

# 创建左侧标签
label_substance = create_label("物质", 0, 0)
label_N = create_label("N", 1, 0)
label_C = create_label("C", 2, 0)
label_CN = create_label("C/N", 3, 0)
label_K = create_label("K", 4, 0)
label_cellulose = create_label("纤维素", 5, 0)
label_hemicellulose = create_label("半纤维素", 6, 0)
label_lignin = create_label("木质素", 7, 0)
label_400 = create_label("400摄氏度碱度", 8, 0)
label_600 = create_label("600摄氏度碱度", 9, 0)

# 创建右侧输入框
entry_substance = create_entry(0, 1)
entry_N = create_entry(1, 1)
entry_C = create_entry(2, 1)
entry_CN = create_entry(3, 1)
entry_K = create_entry(4, 1)
entry_cellulose = create_entry(5, 1)
entry_hemicellulose = create_entry(6, 1)
entry_lignin = create_entry(7, 1)

# 用于显示预测结果的标签(400℃ 和 600℃)
label_pred_400 = create_label("未预测", 8, 1)
label_pred_600 = create_label("未预测", 9, 1)

# 预测按钮(也加入更新列表)
predict_button = tk.Button(root, text="预测", font=default_font, command=predict)
predict_button.grid(row=10, column=0, columnspan=2, pady=10)
widgets_to_update.append(predict_button)

# 定义一个函数,在窗口大小变化时更新所有控件的字体大小
def on_resize(event):
    # 根据窗口当前尺寸与参考尺寸计算缩放比例
    scale_factor = min(event.width / REF_WIDTH, event.height / REF_HEIGHT)
    new_font_size = max(int(BASE_FONT_SIZE * scale_factor), 8)  # 设置最小字体为8
    # 更新所有控件字体
    new_font = (default_font.actual("family"), new_font_size)
    for widget in widgets_to_update:
        widget.config(font=new_font)

# 绑定窗口尺寸变化事件
root.bind("<Configure>", on_resize)

root.mainloop()

本地运行看看效果

2.在huggingface上创建Space

点击new space

填写相关信息后点击Create

3.上传模型、代码、环境配置文件

上传你的模型和相应代码、以及requirements.txt

requirements.txt中直接写需要用到的库

上传文件中需要有个名为 app.py的文件,huggingface会根据这个文件创建网页端应用
为了能够让hf分享别人可以访问的public链接,在app.py中添加参数share=True

# Launch the app with shareable link
if __name__ == "__main__":
    iface.launch(share=True)

为了不让模型自动推理运行,而是让它点击运行时才推理,我们需要将app.py中 live=True设置为False

# Create Gradio interface
iface = gr.Interface(
    fn=predict,
    inputs=[
        gr.Textbox(label="Substance", placeholder="Enter substance"),
        gr.Number(label="N"),
        gr.Number(label="C"),
        gr.Number(label="C/N"),
        gr.Number(label="K"),
        gr.Number(label="Cellulose"),
        gr.Number(label="Hemicellulose"),
        gr.Number(label="Lignin")
    ],
    outputs=[
        gr.Number(label="400℃ Alkalinity"),
        gr.Number(label="600℃ Alkalinity")
    ],
    live=False,  # Disable live prediction to avoid automatic prediction
    title="Alkalinity Prediction Model",
    description="Input relevant data and click the 'Predict' button to get predictions."
)

hf会根据环境配置文件下载相关库并根据app.py创建应用

这样就可以在网页端直接使用模型

将左侧输入值填入后,点击submit后模型输出值显示到右侧

将分享链接分享给好兄弟后,他就可以直接在网页端使用我训练好的简单模型了

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

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

相关文章

51.评论日记

千万不能再挖了&#xff0c;否则整个华夏文明将被改写。_哔哩哔哩_bilibili 2025年4月7日22:13:42

SpringCloud第二篇:注册中心Eureka

注册中心的意义 注册中心 管理各种服务功能包括服务的注册、发现、熔断、负载、降级等&#xff0c;比如dubbo admin后台的各种功能。 有了注册中心&#xff0c;调用关系的变化&#xff0c;画几个简图来看一下。(了解源码可求求: 1791743380) 服务A调用服务B 有了注册中心之后&a…

ES 参数调优

1、refresh_interval 控制索引刷新的时间间隔。增大这个值可以减少I/O操作&#xff0c;从而提升写入性能&#xff0c;但会延迟新文档的可见性 查看 GET /content_erp_nlp_help_202503191453/_settings?include_defaultstrue 动态修改&#xff1a;refresh_interval 是一个动态…

用claude3.7,不到1天写了一个工具小程序(11个工具6个游戏)

一、功能概览和本文核心 本次开发&#xff0c;不是1天干撸&#xff0c;而是在下班后或早起搞的&#xff0c;总体加和计算了一下&#xff0c;大概1天的时间&#xff08;12个小时&#xff09;&#xff0c;平常下班都是9点的衰仔&#xff0c;好在还有双休&#xff0c;谢天谢地。 …

【GeoDa使用】空间自相关分析操作

使用 GeoDa 软件进行空间自相关分析 双击打开 GeoDa 软件 选择 .shp 文件 导入文件 空间权重矩阵&#xff08;*.gal / *.gwt&#xff09;是进行任何空间分析的前提 构建空间权重矩阵 空间权重矩阵&#xff08;Spatial Weights Matrix&#xff09; 是一个用来描述空间对象之间…

C++基于rapidjson的Json与结构体互相转换

简介 使用rapidjson库进行封装&#xff0c;实现了使用C对结构体数据和json字符串进行互相转换的功能。最短只需要使用两行代码即可无痛完成结构体数据转换为Json字符串。 支持std::string、数组、POD数据&#xff08;int,float,double等&#xff09;、std::vector、嵌套结构体…

OpenStack Yoga版安装笔记(十七)安全组笔记

一、安全组与iptables的关系 OpenStack的安全组&#xff08;Security Group&#xff09;默认是通过Linux的iptables实现的。以下是其主要实现原理和机制&#xff1a; 安全组与iptables的关系 OpenStack的安全组规则通过iptables的规则链实现。每条安全组规则会被转换为相应的i…

通义万相2.1 图生视频:为AI绘梦插上翅膀,开启ALGC算力领域新纪元

通义万相2.1图生视频大模型 通义万相2.1图生视频技术架构万相2.1的功能特点性能优势与其他工具的集成方案 蓝耘平台部署万相2.1核心目标典型应用场景未来发展方向 通义万相2.1ALGC实战应用操作说明功能测试 为什么选择蓝耘智算蓝耘智算平台的优势如何通过API调用万相2.1 写在最…

52.个人健康管理系统小程序(基于springbootvue)

目录 1.系统的受众说明 2.开发环境与技术 2.1 MYSQL数据库 2.2 Java语言 2.3 微信小程序技术 2.4 SpringBoot框架 2.5 B/S架构 2.6 Tomcat 介绍 2.7 HTML简介 2.8 MyEclipse开发工具 3.系统分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2 经济可行性 3.1.3 操作…

学习比较JVM篇(六):解读GC日志

一、前言 在之前的文章中&#xff0c;我们对JVM的结构、垃圾回收算法、垃圾回收器做了一些列的讲解&#xff0c;同时也使用了JVM自带的命令行工具进行了实际操作。今天我们继续讲解JVM。 我们学习JVM的目的是为了了解JVM&#xff0c;然后优化对应的参数。那么如何了解JVM运行…

I²S协议概述与信号线说明

IIS协议概述 ​ IS&#xff08;Inter-IC Sound&#xff09;协议&#xff0c;又称 IIS&#xff08;Inter-IC Sound&#xff09;&#xff0c;是一种专门用于数字音频数据传输的串行总线标准&#xff0c;由飞利浦&#xff08;Philips&#xff09;公司提出。该协议通常用于微控制器…

免费Deepseek-v3接口实现Browser-Use Web UI:浏览器自动化本地模拟抓取数据实录

源码 https://github.com/browser-use/web-ui 我们按照官方教程&#xff0c;修订几个环节&#xff0c;更快地部署 步骤 1&#xff1a;克隆存储库 git clone https://github.com/browser-use/web-ui.git cd web-ui Step 2: Set Up Python Environment 第 2 步&#xff1a;设置…

[蓝桥杯] 求和

题目链接 P8772 [蓝桥杯 2022 省 A] 求和 - 洛谷 题目理解 这道题就是公式题&#xff0c;我们模拟出公式后&#xff0c;输出最终结果即可。 本题不难&#xff0c;相信很多同学第一次见到这道题都是直接暴力解题。 两个for循环&#xff0c;测试样例&#xff0c;直接拿下。 #in…

通过Ollama本地部署DeepSeek R1模型(Windows版)

嗨&#xff0c;大家好&#xff0c;我是心海 以下是一份详细的Windows系统下通过Ollama本地部署DeepSeek R1模型的教程&#xff0c;内容简洁易懂&#xff0c;适合新手用户参考 本地部署大模型&#xff0c;就有点像在你自己的电脑或者服务器上&#xff0c;安装并运行这样一个“私…

【C++】vector的底层封装和实现

目录 目录前言基本框架迭代器容量第一个测试&#xff0c;野指针异常第二轮测试&#xff0c;浅拷贝的问题 元素访问修改操作push_backinsert迭代器失效问题 erase 默认成员函数构造函数双重构造引发调用歧义 拷贝构造赋值重载析构函数 源码end 目录 前言 废话不多说&#xff0…

Open CASCADE学习|读取点集拟合样条曲线(续)

问题 上一篇文章已经实现了样条曲线拟合&#xff0c;但是仍存在问题&#xff0c;Tolerance过大拟合成直线了&#xff0c;Tolerance过大头尾波浪形。 正确改进方案 1️⃣ 核心参数优化 通过调整以下参数控制曲线平滑度&#xff1a; Standard_Integer DegMin 3; // 最低阶…

【Django】教程-11-ajax弹窗实现增删改查

【Django】教程-1-安装创建项目目录结构介绍 【Django】教程-2-前端-目录结构介绍 【Django】教程-3-数据库相关介绍 【Django】教程-4-一个增删改查的Demo 【Django】教程-5-ModelForm增删改查规则校验【正则钩子函数】 【Django】教程-6-搜索框-条件查询前后端 【Django】教程…

R语言:气象水文领域的数据分析与绘图利器

R 语言是一门由统计学家开发的用于统计计算和作图的语言&#xff08;a Statistic Language developed for Statistic by Statistician&#xff09;&#xff0c;由 S 语言发展而来&#xff0c;以统计分析功能见长。R 软件是一款集成 了数据操作、统计和可视化功能的优秀的开源软…

Kotlin与HttpClient编写视频爬虫

想用Apache HttpClient库和Kotlin语言写一个视频爬虫。首先&#xff0c;我需要确定用户的具体需求。视频爬虫通常涉及发送HTTP请求&#xff0c;解析网页内容&#xff0c;提取视频链接&#xff0c;然后下载视频。可能需要处理不同的网站结构&#xff0c;甚至可能需要处理动态加载…

图形化编程语言:低代码赛道的技术革命与范式突破

在 2024 年 Gartner 低代码平台魔力象限报告中&#xff0c;传统低代码厂商市场份额增速放缓至 12%&#xff0c;而图形化编程语言赛道融资额同比激增 370%。本文深度剖析低代码平台的技术瓶颈&#xff0c;系统阐释图形化编程语言的核心优势&#xff0c;揭示其如何重构软件开发范…