[万字解析]从零开始使用transformers微调huggingface格式的中文Bert模型的过程以及可能出现的问题

news2024/11/18 18:46:17

系列文章目录

使用transformers中的pipeline调用huggingface中模型过程中可能遇到的问题和修改建议

[万字解析]从零开始使用transformers微调huggingface格式的中文Bert模型的过程以及可能出现的问题


文章目录

  • 系列文章目录
  • 前言
  • 模型与数据集下载
    • 模型下载
    • 数据集下载
  • 数据加载、划分与保存
    • 数据加载
    • 数据集可能造成的问题
    • 数据集划分
    • 数据集保存和加载
  • 数据处理
    • 编码
    • 按文本长度筛选
  • 模型训练与评估
    • 模型加载
    • 加载评价指标
    • 定义评价函数
    • 定义训练参数
    • 定义训练器
    • 模型评估与训练
  • 模型保存与加载
  • 模型预测


前言

在使用huggingface的模型时,很多情况下已有的模型并不能完全满足实际的需求,这种情况下模型微调是常见的解决方式,在本篇文章中将使用transformers针对huggingface格式的中文Bert分类模型为例介绍微调的过程以及解决在微调过程中可能存在的一些问题。

模型与数据集下载

首要步骤是找到合适的模型及数据集,模型和数据集都可以直接在huggingface或者huggingface的镜像上直接搜索
huggingface链接地址:https://huggingface.co
镜像链接地址:https://hf-mirror.com

模型下载

首先在huggingface(或镜像)主页选择Models,然后输入chinese bert可以查询到很多适用于中文的bert模型,在本篇中选择第一个google-bert作为示例。
在这里插入图片描述
也可以直接访问https://huggingface.co/google-bert/bert-base-chinese或https://hf-mirror.com/google-bert/bert-base-chinese
点击进入后查看Model card可以查看chinese-bert的介绍,与之前文章中提到的过程相似,需要下载Files and versions中所有的文件。
在这里插入图片描述
下载完成后将所有内容放在同一个文件夹中,笔者的路径是'/huggingface_model/google-bert/bert-base-chinese',可做参考。

数据集下载

在本篇中以中文评价数据集为例,同理在主页上选择Datasets然后输入ChnSentiCorp,或者直接访问https://hf-mirror.com/datasets/XiangPan/ChnSentiCorp_htl_8k,随便选一个即可(为了防止出现额外的问题,请尽量与本篇保持一致)。
在这里插入图片描述
打开之后可以看到这个数据集是关于酒店的评价,并且数据是不均衡的,数据不均衡的问题在本篇中按下不表,本篇的重点在于跑通整个过程,读者可以自行查看解决数据不均衡的问题。
在这里插入图片描述
选择Files and versions下载数据集,在本篇中只需要下载ChnSentiCorp_htl_8k.csv文件即可,笔者下载到的路径为./datas/ChnSentiCorp_htl_8k/ChnSentiCorp_htl_8k.csv可以作为参考。
在这里插入图片描述

数据加载、划分与保存

数据加载

本篇中使用datasets中的load_dataset加载数据,在进行下一步操作前可以先观察一下数据。
数据加载代码:

from datasets import load_dataset

dataset = load_dataset('csv', data_files='./datas/ChnSentiCorp_htl_8k/ChnSentiCorp_htl_8k.csv')
print(dataset)

运行结果:

DatasetDict({
    train: Dataset({
        features: ['label', 'review'],
        num_rows: 7765
    })
})

从展示效果上可以看到这是一个嵌套两层的字典,根据关键字进一步观察数据。

print(dataset['train']['label'][:2], dataset['train']['review'][:2])

运行结果:

[1, 1] ['距离川沙公路较近,但是公交指示不对,如果是"蔡陆线"的话,会非常麻烦.建议用别的路线.房间较为简单.', '商务大床房,房间很大,床有2M宽,整体感觉经济实惠不错!']

由此可见在本数据集中1表示的是好评,0表示的是差评。

数据集可能造成的问题

在本数据集中6376行中的数据为空,如果不加处理直接使用可能会引起报错,一般来讲,拿到数据集后要检查数据集并需要批量的处理掉空数据,在本篇中用到的数据集只有这一条空数据,可以直接在csv文件中删除后保存后重新读取数据即可。在这里插入图片描述

数据集划分

从csv文件的内容来看数据没有打乱,所以首先需要打乱并显示打乱结果。

dataset['train']= dataset['train'].shuffle()
print(dataset['train']['label'][:5])
print(dataset['train']['review'][:5])

运行结果:

[1, 1, 0, 0, 0]
['酒店地理位置还不错,离外滩、步行街都满近的。我住的是单间,不靠马路,以为会比较安静,哪知窗子外面是酒店内部的一个铁制楼梯,夜晚常有人走过,咣当直响,还有边走边唱歌的,受不了。除了前台小姐面无表情,其它都还行。', '定了高级湖景房,前台给了最高的23层。房间非常舒服,而且早餐也很丰盛。只是看出去的白鹭洲公园景色一般。可能住标准客房更实惠些。', '后悔看了评价。上面老兄的要求也够低的。1)隔音太差2)早餐太差3)卫生间太小4)房间装修太差总的来说就是一经济性酒店。', '总台服务员从我来到走,一直没笑过,很酷!宽带慢的让我想起"猫"的时代!中央空调的声音轰隆隆,热闹!要总台安排第2天一早车去机场,满口答应,退房时没人和我落实,我看着酷酷的成都MM,没问,还是自己拉着箱子走到100米外的路口打车!', '很不怎么样的酒店,根本不象三星级。1、大堂:受理很慢,且服务员连你好都不会说;2、房间:我订的是大床房间,电视机很旧,卫生间里洗澡的地方是用玻璃隔起来的,空间很狭小,而且热水放出来的里面全是棕红色的铁锈,洗澡时水温忽冷忽热;比较变态的是,房间里没有预备网线,你得打电话要,而且电源插座很不合理,如果要插笔记本电源,需要把电视柜往前搬至少50厘米,否则你是找不到插孔的;3、订餐:按照《服务指南》,我打了8003,提示无此号码,于是拨打8006查询,告知我打8033,打过去以后又让我打8031,订个餐有这么麻烦吗?好不容易打通了,要了红烧草鱼、清炒土豆丝和紫菜汤,结果巨难吃,还不如外边随便一个小餐馆做的好吃,反而花了70多元,根本不是三星级酒店厨师做的,让人愤慨!紫菜汤的颜色就像是刷锅水!紫菜也是大片大片的!3、服务:打电话要了网线,过了很久,难道绿洲饭店很大吗?要走那么久?!绿洲饭店我已经住过好几次了,这次是最差劲的!!!给各位推荐立达宾馆,性价比很好,服务也特别好,而且做的菜很有家常口味,吃起来很可口!虽然远了一点,但是打车到市中心,也不过7元钱而已。唯一不足的就是没有宽带连接。补充点评2007年8月16日:还有一点最让人无法忍受:隔音太差了!我的房间隔壁住了个醉汉,那个呻吟声和鼾声,简直就像在同一个房间一样,而且看电视的声音和说话的声音都很清晰!']

在打乱的基础上根据数据集的长度按比例划分训练集和测试集。

length = len(dataset['train'])
train_len = int(length * 0.8)
test_len = int(length * 0.2)
train_dataset = dataset['train'].select(range(train_len))
test_dataset = dataset['train'].select(range(train_len, length))

数据集保存和加载

根据划分好的训练数据和测试数据重新构建数据,将训练数据和测试数据使用datasets.DatasetDict构建成新的数据集。

from datasets import DatasetDict

dataset_dict = DatasetDict({  
    'train': train_dataset,  
    'test': test_dataset  
}) 
print(dataset_dict)

运行结果:

DatasetDict({
    train: Dataset({
        features: ['label', 'review'],
        num_rows: 6212
    })
    test: Dataset({
        features: ['label', 'review'],
        num_rows: 1553
    })
})

使用save_to_disk将处理过的数据保存

dataset_dict.save_to_disk('ChnSentiCorp')

保存好的数据在ChnSentiCorp文件夹中的本地结构如下:
在这里插入图片描述
使用load_from_disk可以加载保存好的数据

from datasets import load_from_disk

dataset = load_from_disk('ChnSentiCorp')
print(dataset)

运行结果:

DatasetDict({
    train: Dataset({
        features: ['label', 'review'],
        num_rows: 6212
    })
    test: Dataset({
        features: ['label', 'review'],
        num_rows: 1553
    })
})

数据处理

在DatasetDict对象中,可以采用map来批量的处理数据,采用filter来批量的筛选数据。

编码

不同模型间的编码方式可以也不同,所以编码的过程需要用到下载好的模型,在transformers中提供了一个自动获取Tokenizer的函数AutoTokenizer。在上文中观察到关键字review是数据中文本部分,所以需要对review字段进行编码。

其中truncation=True指定如果输入的文本长度超过了模型处理的最大长度(这个长度通常在tokenizer初始化时设置),则文本将被截断以适应这个长度。这对于保持批处理的一致性非常重要,因为模型通常要求所有输入具有相同的长度。

在使用map处理数据时最好使用批处理(设置参数batched=True),通过设置batch_size来调整每批的大小,num_proc来设置运行的进程,读者可以根据设备内存适当调整。

def f(data):
    from transformers import AutoTokenizer
    tokenizer = AutoTokenizer.from_pretrained('../huggingface_model/google-bert/bert-base-chinese')
    return tokenizer.batch_encode_plus(data['review'], truncation=True)
    
dataset = dataset.map(f, batched=True, batch_size=1000, num_proc=4, remove_columns=['review'])
print(dataset)

运行结果:

DatasetDict({
    train: Dataset({
        features: ['label', 'input_ids', 'token_type_ids', 'attention_mask'],
        num_rows: 6212
    })
    test: Dataset({
        features: ['label', 'input_ids', 'token_type_ids', 'attention_mask'],
        num_rows: 1553
    })
})

按文本长度筛选

通常情况下序列长度越长,获取到的信息也就越多,但是随着序列长度的增加,运算代价也在增加,但对于本任务中仅需要部分文本信息就可以得出对于旅店的好评或者差评,并且数据量足够大,所以在此可以选择筛选掉超过一定长度的文本(这相当于直接舍弃了超过整个长度的数据,但是在数据量不足的情况下一般会选择截断或者将一整段话拆成几段)。

def f(data):
    return [len(i)<=512 for i in data['input_ids']]
    
dataset=dataset.filter(f, batched=True, batch_size=1000, num_proc=4)
print(dataset)

运行结果:

DatasetDict({
    train: Dataset({
        features: ['label', 'input_ids', 'token_type_ids', 'attention_mask'],
        num_rows: 6054
    })
    test: Dataset({
        features: ['label', 'input_ids', 'token_type_ids', 'attention_mask'],
        num_rows: 1508
    })
})

filter后的效果可以看出整个过程就是按照条件筛选出符合条件的数据,不符合条件的数据会被直接删掉。

模型训练与评估

模型加载

from transformers import AutoModelForSequenceClassification
import torch

model=AutoModelForSequenceClassification.from_pretrained('../huggingface_model/google-bert/bert-base-chinese', num_labels=2)
#统计模型参数量
print(sum([i.nelement() for i in model.parameters()]))

运行结果:

102269186

加载评价指标

如果能够很顺利的访问github,直接使用以下代码会自动下载用于评价精度的accuracy.py,等待下载成功后即可正常运行。

from datasets import load_metric
metric = load_metric('accuracy')

但是如果不能正常访问github或是有在断网情况下使用的应用场景,就会有如下报错。
在这里插入图片描述
出现报错后将报错中的链接复制到浏览器
在这里插入图片描述
这个文件就是所需的accuracy.py,下载后记录下所在地址,笔者的地址是/metrics/accuracy。然后使用下述代码即可执行。特别注意trust_remote_code=True这一参数的设置,这是因为直接加载一个.py文件会存在安全风险,在直接加载.py文件时将会直接不设防的执行其中的代码,所以一般情况下这一行为将会被阻止。所以需要额外的设置这一参数,需要注意的是,这一参数设置为True时需要确保.py文件足够被信任。

from datasets import load_metric
metric = load_metric('./metrics/accuracy', trust_remote_code=True)

定义评价函数

在评价函数中,eval_pred包含预测结果(概率格式)和实际标签,所以运算过程就是先根据预测结果的概率获取预测的标签,再对比预测标签和实际标签获取的模型的精度。

import numpy as np
from transformers.trainer_utils import EvalPrediction
def compute_metrics(eval_pred):
    logits, labels = eval_pred
    logits = logits.argmax(axis=1)
    return metric.compute(predictions=logits, references=labels)

定义训练参数

参数的定义比较简单,对照的注释很好理解。

from transformers import TrainingArguments

args = TrainingArguments(
    output_dir='./output_dir', #定义临时数据保存路径
    evaluation_strategy='steps', #定义测试执行的策略,可取值为no、epoch、steps   
    eval_steps=50, #定义每隔多少个step执行一次测试  
    save_strategy='steps', #定义模型保存策略,可取值为no、epoch、steps 
    save_steps=50, #定义每隔多少个step保存一次
    num_train_epochs=1, #定义共训练几个轮次
    learning_rate=1e-4, #定义学习率
    logging_dir='./logs',            # 日志文件夹  
    logging_steps=50, 
    weight_decay=1e-2, #加入参数权重衰减,防止过拟合
    per_device_eval_batch_size=32, #定义测试和训练时的批次大小
    per_device_train_batch_size=32,
)

定义训练器

训练器的定义只是把前面设置的参数直接传入,容易出问题的是有可能会因为各种包版本不匹配导致评估过程(trainer.evaluate)和训练过程(trainer.train)出现一些奇怪的报错。

from transformers import Trainer
from transformers.data.data_collator import DataCollatorWithPadding
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained('../huggingface_model/google-bert/bert-base-chinese')

trainer = Trainer(
    model=model,
    args=args,
    train_dataset=dataset['train'],
    eval_dataset=dataset['test'],
    compute_metrics=compute_metrics,
    data_collator=DataCollatorWithPadding(tokenizer),
)

可能出现的报错为
在这里插入图片描述
实际上这个完全是因为版本没有对应上,可以通过更新包来实现

pip install --upgrade accelerate
pip install --upgrade transformers
pip install --upgrade torch torchvision torchaudio

如果仍然没有解决,可以参考笔者包的版本组合。

包名版本
accelerate0.34.2
torch2.3.1+cu121
torchaudio2.3.1+cu121
torchvision0.18.1+cu121
transformers4.45.1

模型评估与训练

在训练之前先观察一下如果不进行训练,直接使用模型会有怎样的效果
模型评估:

trainer.evaluate()

运行结果:

{'eval_loss': 0.888075590133667,
 'eval_model_preparation_time': 0.0,
 'eval_accuracy': 0.3103448275862069,
 'eval_runtime': 21.5519,
 'eval_samples_per_second': 69.971,
 'eval_steps_per_second': 2.227}

从运行结果上来看,精度还没有到50%,比随机分的效果还要差一点。所以训练是有必要的(有些情况下,比如可以找到类似任务的模型的情况下,如果在自己的数据集上能有较好的效果就可以不进行训练而直接使用)
模型训练:

trainer.train()

运行结果:
在这里插入图片描述
由于我们设置了每隔50个step保存一次,并且保存路径为./output_dir,所以每次的检查点将保存至这个文件夹中。
在这里插入图片描述
训练结束后再进行评估

trainer.evaluate()

运行结果:

{'eval_loss': 0.48144271969795227,
 'eval_model_preparation_time': 0.0,
 'eval_accuracy': 0.7360742705570292,
 'eval_runtime': 153.5957,
 'eval_samples_per_second': 9.818,
 'eval_steps_per_second': 0.313,
 'epoch': 1.0}

可以看到在训练后精度得到了显著提高,虽然仍然不算特别高,但由于我们只训练了一轮,这样的结果已经足够令人满意了,如果想得到更高的精度需要更多的训练轮数。

模型保存与加载

模型的保存直接调用save_model,传入的参数是输出目录
模型保存

trainer.save_model(output_dir='./output_dir/save_model')

模型加载
在加载模型前要注意,需要从原模型文件夹中将词表复制到保存的模型的文件夹中,即把'../huggingface_model/google-bert/bert-base-chinese'目录下的vocab.txt复制到'./output_dir/save_model'

复制后'./output_dir/save_model'路径下应该存在以下四个文件
在这里插入图片描述
模型加载:
加载过程和加载原模型一致,尤其要注意的是执行这一步是必须的 model.to('cuda'),因为数据和模型必须在同一个设备上,如果没有GPU可用可以去掉或者改为 model.to('cpu')

model_name = './output_dir/save_model'

tokenizer = AutoTokenizer.from_pretrained(model_name)  
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)

model.to('cuda')

模型预测

model.eval()

for i, data in enumerate(trainer.get_eval_dataloader()): 
    out = model(**data)
    out = out['logits'].argmax(dim=1)
    for i in range(8):
        print(tokenizer.decode(data['input_ids'][i], skip_special_tokens =True))
        print(tokenizer.decode(data['input_ids'][i], skip_special_tokens =False))
        print('label=', data['labels'][i].item())
        print('predict=', out[i].item())
    break

运行结果:
在这里插入图片描述

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

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

相关文章

单细胞转录组 —— simpleaf 原始数据处理

单细胞转录组 —— 原始数据处理实战&#xff08;simpleaf&#xff09; 前言 Alevin-fry 是一个快速、准确且内存节约的单细胞和单核数据处理工具。 Simpleaf 是用 Rust 编写的程序&#xff0c;它提供了一个统一且简化的界面&#xff0c;用于通过 alevin-fry 流程处理一些最…

软件设计师——系统基础开发

&#x1f4d4;个人主页&#x1f4da;&#xff1a;秋邱-CSDN博客☀️专属专栏✨&#xff1a;软考——软件设计师&#x1f3c5;往期回顾&#x1f3c6;&#xff1a;软件设计师——信息安全&#x1f31f;其他专栏&#x1f31f;&#xff1a;C语言_秋邱 ​ 一、软件工程概述 1.1、考…

【Linux】man手册安装使用

目录 man(manual,手册) 手册安装: 章节区分&#xff1a; 指令参数: 使用场景&#xff1a; 手册内容列表: 手册查看快捷键: 实例: 仍致谢:Linux常用命令大全(手册) – 真正好用的Linux命令在线查询网站 提供的命令查询 在开头先提醒一下:在 man 手册中退出的方法很简单…

数字IC/FPGA AMBA总线 (内容参考B站UP主数字逻辑君)

1、 串行总线 SPI IIC UART Fsmc &#xff08;串行总线本文不再赘述&#xff0c;可以参考作者其他文章&#xff09; 总线简介&#xff1a; AMBA常用的系统总线&#xff1a;AHB&#xff0c;ASB&#xff0c;APB&#xff0c;AXI总线&#xff0c;一个Soc和外部的外设不可能每…

zigbee学习

24.10.7学习目录 一.简介1.分层2.zstack通信 一.简介 其是一种新兴的短距离无线通信技术&#xff0c;用于传感控制应用&#xff1b; 特性&#xff1a; 低功耗&#xff0c;比wifi蓝牙功耗更低&#xff1b;低成本&#xff1b;低速率&#xff1b;近距离&#xff1b;短时延&…

老外发微信时说“I‘ll ping you”是什么意思?发微信怎么用英语说柯桥学英语到哪里?

“发信息”还可以怎么说&#xff1f; 其实很简单&#xff0c;message做动词时&#xff0c;可以直接表达&#xff1a;发信息 ▼ &#x1f330;举个例子 I messaged him yesterday but havent had a reply. 昨天我给他发了短信&#xff0c;但没有回音。 我们现在常说的“发信…

使用Python批量修改文件修改日期为随机的6到8月份

使用Python批量修改文件修改日期为随机的6到8月份 每当雪花飘起的时候&#xff0c;总有一股抹不去的情节&#xff0c;会想起儿时雪天的记忆&#xff0c;虽然模糊但也清晰。那时每年的冬季很冷&#xff0c;但依然喜欢飘雪的日子&#xff0c;看着满天迷蒙飘舞的雪花总有想不完的心…

生成树实验

1 生成树关键点&#xff0c; 第一树根&#xff0c;第二在每个非根桥找root端口 第三 在每个物理片段找指定网桥&#xff0c;第四指定网桥对应的端口就是指定端口 bpdu 比较的方式 root 桥&#xff0c;到root 桥的路径开销&#xff0c;指定桥&#xff0c;指定端口&#x…

双登股份再战IPO:数据打架,实控人杨善基千万元股权激励儿子

撰稿|行星 来源|贝多财经 近日&#xff0c;双登集团股份有限公司&#xff08;下称“双登股份”&#xff09;递交招股书&#xff0c;准备在港交所主板上市&#xff0c;中金公司、建银国际、华泰国际为其联席保荐人。 贝多财经了解到&#xff0c;这并非双登股份首次向资本市场…

谷歌AI大模型Gemini API快速入门及LangChain调用视频教程

1. 谷歌Gemini API KEY获取及AI Studio使用 要使用谷歌Gemini API&#xff0c;首先需要获取API密钥。以下是获取API密钥的步骤&#xff1a; 访问Google AI Studio&#xff1a; 打开浏览器&#xff0c;访问Google AI Studio。使用Google账号登录&#xff0c;若没有账号&#xf…

体制内的必须要知道的“人情世故”及职场礼仪

最近&#xff0c;一位新来的小姑娘在参加活动的时候给外来领导带路&#xff0c;结果到跟前时&#xff0c;没有及时退让&#xff0c;夹在了自己领导与外来领导之间&#xff0c;妨碍了两位领导握手&#xff0c;下来后被一顿狠批。这其实是新人不太懂职场礼仪导致的&#xff0c;笔…

OpenCV库模块解析

1.OpenCV库每个模块解析 2.OpenCV的常用函数 它为计算机视觉应用程序提供了一个通用的基础设施&#xff0c;并加速了在商业产品中使用机器感知。作为BSD许可的产品&#xff0c;OpenCV使企业可以很容易地利用和修改代码。该库拥有超过2500个优化算法&#xff0c;其中包括经典和最…

大数据-158 Apache Kylin 安装配置详解 集群模式启动

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

无线麦克风什么牌子的音质效果好?选购中必须警惕劣质产品

在音频设备不断推陈出新、日益丰富多样的今天&#xff0c;无线领夹麦克风以其独有的优点崭露头角。它的设计非常精巧&#xff0c;佩戴起来既舒适又方便&#xff0c;并且在各种不同的环境下都能保证音质稳定以及传输效果良好。 无论是在户外进行拍摄、室内开展直播&#xff0c;…

uniapp学习(003-3 vue3学习 Part.3)

零基础入门uniapp Vue3组合式API版本到咸虾米壁纸项目实战&#xff0c;开发打包微信小程序、抖音小程序、H5、安卓APP客户端等 总时长 23:40:00 共116P 此文章包含第21p-第p25的内容 文章目录 双向绑定的实现原理例子 计算属性例子1双向绑定格式改成计算属性 例子2 watchwatc…

STM32 -- USB通信 ( 虚拟串口)

本篇操作: 通过CubeMX Keil&#xff0c;配置STM32作为USB设备端&#xff0c;与电脑进行通信&#xff08;CDC&#xff09;&#xff1b;通用带USB功能的 STM32 芯片 &#xff08;如F1、F4等&#xff0c;系统时钟配置不同&#xff0c;代码通用&#xff09;。 目录 一、 STM32内…

高质量带货短视频素材来源推荐

在抖音带货时&#xff0c;寻找高质量视频素材至关重要。今天&#xff0c;我为大家分享五个可以下载高清无水印带货短视频素材的网站&#xff0c;帮助你轻松获取灵感和素材&#xff01; 蛙学网 蛙学网作为国内领先的短视频素材平台&#xff0c;提供多种类的带货短视频素材。无论…

[QT GUI Tips] Qt creator + PySide6 如何让图像控件的尺寸变化和窗口一致

前言&#xff1a;【这是个AI不会回答的问题】 Qt Creator 新的版本又发出了&#xff0c;Pyside6 有很多新功能。但是&#xff0c;一些传统的方法要被淘汰了。 一个经典的例子是&#xff1a; 我有个一个图像要显示在Form里面的图像控件上&#xff0c;OK&#xff0c; 我现在拖…

操作系统-系统调用

应用程序调用printf(),会触发系统调用write() 1、概念 操作系统服务的编程接口&#xff0c;通常由高级语言编写&#xff08;C/C&#xff09;&#xff0c;程序访问通常是通过高层次的API接口而不是直接进行系统调用。 2、三种最常用的应用程序编程接口&#xff08;API&#xf…

从零开始:网页在线制作入门指南

如果你对网页在线制作感兴趣&#xff0c;想学习如何从零开始创建一个网页&#xff0c;这个教程将带你了解基础步骤、所需工具以及如何将设计交付给开发人员的完整过程。接下来&#xff0c;让我们开始吧&#xff01; 一、 明确目标群体与网站用途 在启动网页制作之前&#xff…