【LLM】浅析chatglm的sft+p-tuning v2

note

  • GLM将针对不同类型下游任务的预训练目标统一为了自回归填空,结合了混合的注意力机制和新的二维位置编码。
  • 本文浅析sft,并基于GLM在广告描述数据集上进行sft+p-tuning代码的数据流讲解

文章目录

  • note
  • 零、ChatGLM2模型
  • 一、Supervised fine-tuning
    • 1. 数据样本的准备
    • 2. stanford_alpaca的dataset类
    • 3. 数据格式、计算loss的数据
  • 二、P-Tuning v2的数据流
    • 1. 数据准备
    • 2. P-Tuning v2微调
    • 3. 模型推理
  • 三、垂直领域训练注意事项
  • Reference

零、ChatGLM2模型

后面对chatglm2进行sft微调,这里顺带着先介绍下glm2:
在这里插入图片描述

  • chatglm-6b:https://github.com/THUDM/ChatGLM-6B
  • chatglm2-6b:https://github.com/THUDM/ChatGLM2-6B
  • chatglm130:https://github.com/THUDM/GLM-130B

在这里插入图片描述

  • 自回归空格填充任务:
    • 初始文本输入:x1, x2,x3,x4,x5,x6
    • 随机掩码mask
      • PartA 部分:x1,x2,M,x4,M ,其中M表示mask的跨度
      • PartB 部分: S,x5,x6,S,x3(刚才mask掉的那几坨,随机排序后,对起始位置加入token)
      • 拼接PartA和PartB
  • 自注意机制(chatglm在Q、K中加入了RoPE位置信息): Q = W q X K = W k X V = W v X Attention ⁡ ( Q , K , V , A ) = softmax ⁡ ( Q K T d k ) V \begin{aligned} Q & =W_q X \\ K & =W_k X \\ V & =W_v X \\ \operatorname{Attention}(Q, K, V, A) & =\operatorname{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V \end{aligned} QKVAttention(Q,K,V,A)=WqX=WkX=WvX=softmax(dk QKT)V
  • mask:chatglm6b使用prefix-LM的mask,对于输入的前缀使用双向注意力,对于后续的生成部分则是causal mask
    • PartA部分内的各token可以互相注意到
    • PartB部分内的tokens可以注意到PartA和PartB中已经生成的token

在这里插入图片描述

    def get_masks(self, input_ids, past_key_values, padding_mask=None):batch_size, seq_length = input_ids.shapefull_attention_mask = torch.ones(batch_size, seq_length, seq_length, device=input_ids.device)full_attention_mask.tril_()past_length = 0if past_key_values:past_length = past_key_values[0][0].shape[0]if past_length:full_attention_mask = torch.cat((torch.ones(batch_size, seq_length, past_length,device=input_ids.device), full_attention_mask), dim=-1)if padding_mask is not None:full_attention_mask = full_attention_mask * padding_mask.unsqueeze(1)if not past_length and padding_mask is not None:full_attention_mask -= padding_mask.unsqueeze(-1) - 1full_attention_mask = (full_attention_mask < 0.5).bool()full_attention_mask.unsqueeze_(1)return full_attention_mask

一、Supervised fine-tuning

sft就是下面的第一个环节,使用指令数据做有监督精调 (supervised fine-tuning)。

在这里插入图片描述

1. 数据样本的准备

参考stanford_alpaca的sft:

  • 整理的数据有三列:instruction、input、output。
  • Instruction和input通过prompt组搞在一起,为sourse;output换为target
  • 把source和target和token.eos_token_id直接拼接在一起,这个时候暂时叫sentence。
  • 然后把sentence通过tokenizer转换成input_ids。
  • 最后一步,要把input_ids复制一份,叫labels。然后把labels前面的位置,即source对应的tokenid,全部变成-100。
  • 那么这个时候,一个面向sft任务的input_ids和labels就已经构造好了。

在这个任务里面,使用的就是transformers的DataCollatorForSeq2Seq。这个data_collator任务很简单:就是让每一个batch内的input_ids和labels都长度对齐。

def _tokenize_fn(strings: Sequence[str], tokenizer: transformers.PreTrainedTokenizer) -> Dict:"""Tokenize a list of strings."""tokenized_list = [tokenizer(text,return_tensors="pt",padding="longest",max_length=tokenizer.model_max_length,truncation=True,)for text in strings]input_ids = labels = [tokenized.input_ids[0] for tokenized in tokenized_list]input_ids_lens = labels_lens = [tokenized.input_ids.ne(tokenizer.pad_token_id).sum().item() for tokenized in tokenized_list]return dict(input_ids=input_ids,labels=labels,input_ids_lens=input_ids_lens,labels_lens=labels_lens,)def preprocess(sources: Sequence[str],targets: Sequence[str],tokenizer: transformers.PreTrainedTokenizer,
) -> Dict:"""Preprocess the data by tokenizing."""examples = [s + t for s, t in zip(sources, targets)]examples_tokenized, sources_tokenized = [_tokenize_fn(strings, tokenizer) for strings in (examples, sources)]input_ids = examples_tokenized["input_ids"]labels = copy.deepcopy(input_ids)for label, source_len in zip(labels, sources_tokenized["input_ids_lens"]):label[:source_len] = IGNORE_INDEXreturn dict(input_ids=input_ids, labels=labels)

2. stanford_alpaca的dataset类

class SupervisedDataset(Dataset):"""Dataset for supervised fine-tuning."""def __init__(self, data_path: str, tokenizer: transformers.PreTrainedTokenizer):super(SupervisedDataset, self).__init__()logging.warning("Loading data...")list_data_dict = utils.jload(data_path)logging.warning("Formatting inputs...")prompt_input, prompt_no_input = PROMPT_DICT["prompt_input"], PROMPT_DICT["prompt_no_input"]sources = [prompt_input.format_map(example) if example.get("input", "") != "" else prompt_no_input.format_map(example)for example in list_data_dict]targets = [f"{example['output']}{tokenizer.eos_token}" for example in list_data_dict]logging.warning("Tokenizing inputs... This may take some time...")data_dict = preprocess(sources, targets, tokenizer)self.input_ids = data_dict["input_ids"]self.labels = data_dict["labels"]def __len__(self):return len(self.input_ids)def __getitem__(self, i) -> Dict[str, torch.Tensor]:return dict(input_ids=self.input_ids[i], labels=self.labels[i])

3. 数据格式、计算loss的数据

简单分析hf的trainer:hugggingface自带的trainer类中参数如下:

    def __init__(self,model: Union[PreTrainedModel, nn.Module] = None,args: TrainingArguments = None,data_collator: Optional[DataCollator] = None,train_dataset: Optional[Dataset] = None,eval_dataset: Optional[Union[Dataset, Dict[str, Dataset]]] = None,tokenizer: Optional[PreTrainedTokenizerBase] = None,model_init: Optional[Callable[[], PreTrainedModel]] = None,compute_metrics: Optional[Callable[[EvalPrediction], Dict]] = None,callbacks: Optional[List[TrainerCallback]] = None,optimizers: Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR] = (None, None),preprocess_logits_for_metrics: Optional[Callable[[torch.Tensor, torch.Tensor], torch.Tensor]] = None,)

hf自带的trainer中的compute_loss函数中,用到了标签平滑的正则化(将真实标签的概率分布进行平滑,减少模型过拟合):
在这里插入图片描述
对于上面trainer类的参数data_collator,对于encoder和decoder模型都是不同的:比如前者的bert模型,用于ner词性标注任务时:

BertForTokenClassification((bert): BertModel((embeddings): BertEmbeddings((word_embeddings): Embedding(28996, 768, padding_idx=0)(position_embeddings): Embedding(512, 768)(token_type_embeddings): Embedding(2, 768)(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)(dropout): Dropout(p=0.1, inplace=False))(encoder): BertEncoder((layer): ModuleList((0-11): 12 x BertLayer((attention): BertAttention((self): BertSelfAttention((query): Linear(in_features=768, out_features=768, bias=True)(key): Linear(in_features=768, out_features=768, bias=True)(value): Linear(in_features=768, out_features=768, bias=True)(dropout): Dropout(p=0.1, inplace=False))(output): BertSelfOutput((dense): Linear(in_features=768, out_features=768, bias=True)(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)(dropout): Dropout(p=0.1, inplace=False)))(intermediate): BertIntermediate((dense): Linear(in_features=768, out_features=3072, bias=True)(intermediate_act_fn): GELUActivation())(output): BertOutput((dense): Linear(in_features=3072, out_features=768, bias=True)(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)(dropout): Dropout(p=0.1, inplace=False))))))(dropout): Dropout(p=0.1, inplace=False)(classifier): Linear(in_features=768, out_features=9, bias=True)
)

对于上面这种encoder模型的data_collator定义如下(比如我们要做文本分类,每个数据样本即对应一个文本序列+一个label):

from transformers import BertTokenizer
from torch.utils.data import DataLoadertokenizer = BertTokenizer.from_pretrained('bert-base-cased')class TextClassificationDataset(Dataset):def __init__(self, texts, labels, max_length):self.texts = textsself.labels = labelsself.max_length = max_lengthdef __len__(self):return len(self.texts)def __getitem__(self, index):text = self.texts[index]label = self.labels[index]input_ids, attention_mask = self.encode_text(text)return {'input_ids': input_ids, 'attention_mask': attention_mask, 'label': label}def encode_text(self, text):input_ids = tokenizer.encode(text, add_special_tokens=True, max_length=self.max_length, truncation=True)attention_mask = [1] * len(input_ids)padding_length = self.max_length - len(input_ids)input_ids = input_ids + [0] * padding_lengthattention_mask = attention_mask + [0] * padding_lengthreturn input_ids, attention_maskdef collate_fn(batch):input_ids = [item['input_ids'] for item in batch]attention_mask = [item['attention_mask'] for item in batch]labels = [item['label'] for item in batch]label_map = {label: i for i, label in enumerate(set(labels))}encoded_labels = [label_map[label] for label in labels]return {'input_ids': input_ids, 'attention_mask': attention_mask, 'labels': encoded_labels}train_dataset = TextClassificationDataset(texts=train_texts, labels=train_labels, max_length=128)
train_dataloader = DataLoader(train_dataset, batch_size=32, collate_fn=collate_fn)

后者的gpt是next token prediction,以chatglm为栗子,特点是加入了ROPE旋转位置编码、使用RMSNorm正则化等操作:

ChatGLMForConditionalGeneration((transformer): ChatGLMModel((embedding): Embedding((word_embeddings): Embedding(65024, 4096))(rotary_pos_emb): RotaryEmbedding()(encoder): GLMTransformer((layers): ModuleList((0-27): 28 x GLMBlock((input_layernorm): RMSNorm()(self_attention): SelfAttention((query_key_value): QuantizedLinear()(core_attention): CoreAttention((attention_dropout): Dropout(p=0.0, inplace=False))(dense): QuantizedLinear())(post_attention_layernorm): RMSNorm()(mlp): MLP((dense_h_to_4h): QuantizedLinear()(dense_4h_to_h): QuantizedLinear())))(final_layernorm): RMSNorm())(output_layer): Linear(in_features=4096, out_features=65024, bias=False))
)

对于上面这种decoder模型,我们的data_collator定义如下(TextGenerationDataset对输入文本进行编码,并且将目标序列往后移动一位以便预测,dataloader将数据集分为多个mini-batch,collate_fn函数对每个mini-batch数据进行自定义组合):

from transformers import GPT2Tokenizer
from torch.utils.data import Dataset, DataLoader
from torch.nn.utils.rnn import pad_sequencetokenizer = GPT2Tokenizer.from_pretrained('gpt2')class TextGenerationDataset(Dataset):def __init__(self, texts, max_length):self.texts = textsself.max_length = max_lengthdef __len__(self):return len(self.texts)def __getitem__(self, index):text = self.texts[index]input_ids, attention_mask = self.encode_text(text)return {'input_ids': input_ids, 'attention_mask': attention_mask}def encode_text(self, text):input_ids = tokenizer.encode(text, add_special_tokens=True, max_length=self.max_length, truncation=True)attention_mask = [1] * len(input_ids)padding_length = self.max_length - len(input_ids)input_ids = input_ids + [tokenizer.pad_token_id] * padding_lengthattention_mask = attention_mask + [0] * padding_lengthreturn input_ids, attention_maskdef collate_fn(batch):input_ids = [torch.tensor(item['input_ids'], dtype=torch.long) for item in batch]attention_mask = [torch.tensor(item['attention_mask'], dtype=torch.long) for item in batch]input_ids = pad_sequence(input_ids, batch_first=True, padding_value=tokenizer.pad_token_id)attention_mask = pad_sequence(attention_mask, batch_first=True, padding_value=0)return {'input_ids': input_ids, 'attention_mask': attention_mask}train_dataset = TextGenerationDataset(texts=train_texts, max_length=128)
train_dataloader = DataLoader(train_dataset, batch_size=32, collate_fn=collate_fn)

在计算loss时也会将input_id向后移动1位后作为label,比如下面chatglm2-6b的源码:

  • lm_logits:初始[batch_size, sequence_length, vocab_size],减1是将最后一个位置的预测结果去掉
  • labels:初始[batch_size, sequence_length],下面代码的labels[..., 1:]即第1个(0开始计算)维度从位置1开始取
  • 最后计算nll_losssmoothed_loss的加权和,作为loss值
    def __call__(self, model_output, labels, shift_labels=False):logits = model_output["logits"] if isinstance(model_output, dict) else model_output[0]if shift_labels:logits = logits[..., :-1, :].contiguous()labels = labels[..., 1:].contiguous()log_probs = -nn.functional.log_softmax(logits, dim=-1)if labels.dim() == log_probs.dim() - 1:labels = labels.unsqueeze(-1)padding_mask = labels.eq(self.ignore_index)# In case the ignore_index is -100, the gather will fail, so we replace labels by 0. The padding_mask# will ignore them in any case.labels = torch.clamp(labels, min=0)nll_loss = log_probs.gather(dim=-1, index=labels)# works for fp16 input tensor too, by internally upcasting it to fp32smoothed_loss = log_probs.sum(dim=-1, keepdim=True, dtype=torch.float32)nll_loss.masked_fill_(padding_mask, 0.0)smoothed_loss.masked_fill_(padding_mask, 0.0)# Take the mean over the label dimensions, then divide by the number of active elements (i.e. not-padded):num_active_elements = padding_mask.numel() - padding_mask.long().sum()nll_loss = nll_loss.sum() / num_active_elementssmoothed_loss = smoothed_loss.sum() / (num_active_elements * log_probs.shape[-1])return (1 - self.epsilon) * nll_loss + self.epsilon * smoothed_loss

二、P-Tuning v2的数据流

项目:基于广告数据集,chatglm2的p-tuning v2微调
背景:sft指令微调时为了加快训练, Parameter-Efficient Model Adaptation,所以经常伴随着prompt tuning、lora tuning、p-tuning v2等peft操作。下面以chatglm2官方的p-tuning v2为例介绍。

1. 数据准备

ADGEN 数据集任务为根据输入(content)生成一段广告词(summary)。

{"content": "类型#上衣*版型#宽松*版型#显瘦*图案#线条*衣样式#衬衫*衣袖型#泡泡袖*衣款式#抽绳","summary": "这件衬衫的款式非常的宽松,利落的线条可以很好的隐藏身材上的小缺点,穿在身上有着很好的显瘦效果。领口装饰了一个可爱的抽绳,漂亮的绳结展现出了十足的个性,配合时尚的泡泡袖型,尽显女性甜美可爱的气息。"
}

2. P-Tuning v2微调

  • train.sh 中的 PRE_SEQ_LENLR 分别是 soft prompt 长度和训练的学习率,可以进行调节以取得最佳的效果。
  • P-Tuning-v2 方法会冻结全部的模型参数,可通过调整 quantization_bit 来被原始模型的量化等级,不加此选项则为 FP16 精度加载。
  • glm2源码中的data_collator使用了transformer的DataCollatorForSeq2Seq,其实很多时候直接用这个就行了,不用像上面1.1中的stanford_alpaca中一样去自定义:
from transformers import DataCollatorForSeq2Seqdata_collator = DataCollatorForSeq2Seq(tokenizer,model=model,label_pad_token_id=label_pad_token_id,pad_to_multiple_of=None,padding=False)

3. 模型推理

在 P-tuning v2 训练时模型只保存 PrefixEncoder 部分的参数(继承了trainerPrefixTrainer类,重写了父类的_save函数),所以在推理时需要同时加载原 ChatGLM2-6B 模型以及 PrefixEncoder 的权重,因此需要指定 evaluate.sh 中的参数:

--model_name_or_path THUDM/chatglm2-6b
--ptuning_checkpoint $CHECKPOINT_PATH

如果是,只需要跟之前一样设定 model_name_or_path

--model_name_or_path $CHECKPOINT_PATH

评测指标为中文 Rouge score 和 BLEU-4。

三、垂直领域训练注意事项

如PowerLawGLM的训练。(怎么做法律条文的准确输出)

  • 对pretrained 进行法文垂直场景的增量微调。 收集大量的法文数据(网络,离线):对GLM130基座模型进行增量预训练,先把垂直场景的法律条文数据注入到pretrained 模型。
  • 对齐法律场景对齐:用监督数据做SFT微调(百万级真实法律条文的数据)。
  • 解决出现的幻觉问题(输出不存在的法律条文): (重点工程优化,比如做基于检索的条文输出)可参考Langchain+LLM的输出方式(外挂知识库)。

Reference

[1] GLM: General Language Model Pretraining with Autoregressive Blank Infilling ,ACL2022
[2] https://aclanthology.org/2022.acl-long.26.pdf
[3] GLM论文精读-自回归填空的通用语言模型

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

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

相关文章

C#时间轴曲线图形编辑器开发2-核心功能实现

目录 三、关键帧编辑 1、新建Winform工程 &#xff08;1&#xff09;界面布局 &#xff08;2&#xff09;全局变量 2、关键帧添加和删除 &#xff08;1&#xff09;鼠标在曲线上识别 &#xff08;2&#xff09;键盘按键按下捕捉 &#xff08;3&#xff09;关键帧添加、删…

Unity自定义后处理——用偏导数求图片颜色边缘

大家好&#xff0c;我是阿赵。   继续介绍屏幕后处理效果的做法。这次介绍一下用偏导数求图形边缘的技术。 一、原理介绍 先来看例子吧。   这个例子看起来好像是要给模型描边。之前其实也介绍过很多描边的方法&#xff0c;比如沿着法线方向放大模型&#xff0c;或者用Ndo…

Palo Alto Networks 智能网络安全保护任何地方的用户、应用和数据

Palo Alto Networks 不仅能够为数字企业提供当下所需的网络安全服务&#xff0c;还能为日后的工作打好安全基础&#xff0c;让企业无需在二者间权衡和纠结&#xff0c;这样的网络安全合作伙伴仅此一家。我们承诺将双管齐下&#xff0c;在保障数字企业的安全方面绝不妥协退让。下…

专题-【排序比较】

时间最好&#xff1a;直接和冒泡&#xff0c;n 堆排序和归并时间固定&#xff1a;nlog2n 空间&#xff1a;归并&#xff0c;n&#xff1b;快速&#xff0c;log2n 稳定&#xff1a;直冒归基

Sharding-JDBC分布式事务详解与实战

&#x1f680; ShardingSphere &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&…

K8s卷存储详解(二)

K8s卷存储详解&#xff08;二&#xff09; 持久卷持久卷&#xff08;PV&#xff09;持久卷申领&#xff08;PVC&#xff09;存储类&#xff08;StorageClass&#xff09;存储制备器&#xff08;Provisioner&#xff09;PV和PVC的生命周期持久卷的类型CSI 卷快照CSI 卷克隆 投射…

多租户的低代码平台,Saas开发平台:MateCloud

简介 MateCloud是一款基于Spring Cloud Alibaba的微服务架构。目前已经整合Spring Boot 2.7.0、 Spring Cloud 2021、Spring Cloud Alibaba 2021、Spring Security Oauth2、Feign、Dubbo、JetCache、RocketMQ等&#xff0c;支持多租户的低代码平台&#xff0c;Saas平台开发套件…

Elasticsearch-增删改查数据工作原理

集群 集群的基本概念&#xff1a; 集群&#xff1a;ES 集群由一个或多个 Elasticsearch 节点组成&#xff0c;每个节点配置相同的 cluster.name 即可加入集群&#xff0c;默认值为 “elasticsearch”。节点&#xff1a;一个 Elasticsearch 服务启动实例就是一个节点&#xff…

【Linux】线程同步条件变量

目录 1 线程同步的引入 2 条件变量&线程同步&竞争条件的概念 3 条件变量相关函数 初始化 销毁 等待条件满足 唤醒等待 4 demo代码——理解条件变量&线程同步 5 为什么 pthread_cond_wait 需要互斥量? 6 条件变量使用规范 1 线程同步的引入 例子生活化&…

【多任务编程-线程通信】

进程/线程通信的方式 某些应用程序中&#xff0c;进程/进程和线程/线程之间不可避免的进行通信&#xff0c;进行消息传递&#xff0c;数据共享等 同一进程的线程之间通信方式包括Windows中常用Event, Message等。 不同进程之间的通信可以利用Event, FileMapping(内存共享), W…

24考研数据结构-栈和队列的应用

目录 3.3.1栈在括号匹配中的应用流程图算法代码 3.3.2栈在表达式求值中的应用1. 中缀表达式 (需要界限符)2. 后缀表达式 (逆波兰表达式)中缀表达式转后缀表达式-手算重点&#xff1a;中缀表达式转后缀表达式-机算重点&#xff1a;后缀表达式的计算—机算 3.前缀表达式 (波兰表达…

杭电oj Simple Set Problem 双指针 尺取法 满注释版

&#x1f468;‍&#x1f3eb; 题目地址 输入 3 2 1 6 3 -7 7 10 4 9 -5 -9 2 8 5 4 3 3 8 2 10 8 1 -7 3 1 6 10 1 1 9输出 1 15 0使用快读&#xff0c;避免使用 Arrays.fill() 按需初始化 避免卡常 &#x1f351; 思路 &#x1f37a; AC code import java.io.*; import ja…

机器学习李宏毅学习笔记39

文章目录 前言一、大模型的发展趋势二、KNN LM总结 前言 大模型大资料 大模型的顿悟时刻 一、大模型的发展趋势 随数据量增加&#xff0c;模型可以从量变达到质变&#xff0c;从某一刻开始突然学会东西。 当成为大模型时&#xff0c;分数会从0,0突然变成100&#xff0c;完成“…

【Matplotlib 绘制折线图】

使用 Matplotlib 绘制折线图 在数据可视化中&#xff0c;折线图是一种常见的图表类型&#xff0c;用于展示随着变量的变化&#xff0c;某个指标的趋势或关系。Python 的 Matplotlib 库为我们提供了方便易用的功能来绘制折线图。 绘制折线图 下面的代码展示了如何使用 Matplo…

基于Centos 7虚拟机的磁盘操作(添加磁盘、分区、格式分区、挂载)

目录 一、添加硬盘 二、查看新磁盘 三、磁盘分区 3.1新建分区 3.2 格式分区 3.3 挂载分区 3.4 永久挂载新分区 3.5 取消挂载分区 一、添加硬盘 1.在虚拟机处选择编辑虚拟机设置&#xff0c;然后选择添加 2.选择硬盘&#xff0c;然后选择下一步 3.默认即可&#xff0c;下一步…

【6】toLocaleString、toLocaleDateString、toLocaleTimeString等toLocale系列方法的使用及案例

一、介绍 在当今前端开发的领域里&#xff0c;快速、高效的项目构建工具以及使用最新技术栈是非常关键的。ViteVue3 组合为一体的项目实战示例专栏将带领你深入了解和掌握这一最新的前端开发工具和框架。 作为下一代前端构建工具&#xff0c;Vite 在开发中的启动速度和热重载…

C语言:动态内存管理

文章目录 一、动态内存函数1. malloc2. calloc3. realloc4. free 二、常见的错误1.malloc或calloc开辟的空间未检查2.越界访问3.对非malloc和calloc开辟的空间&#xff0c;用free释放4.对同一块动态内存多次释放5.用free释放动态内存的一部分 三、通讯录(动态版本改写)总结 一、…

uni-app:模态框的实现(弹窗实现)

效果图 代码 标签 <template><view><!-- 按钮用于触发模态框的显示 --><button click"showModal true">显示模态框</button><!-- 模态框组件 --><view class"modal" v-if"showModal"><view cla…

探索APP开发的新趋势:人工智能和大数据的力量

随着5G技术的不断发展&#xff0c;人工智能和大数据将会更加广泛的应用于我们生活和工作中&#xff0c;作为 APP开发公司&#xff0c;应该及时的对新技术进行研发&#xff0c;进而更好的为用户服务。目前 APP开发已经不是传统的软件开发了&#xff0c;而是向移动互联网转型&…

iPhone 开机停留在苹果logo画面(已解决)

一、问题 如下图&#xff0c;开不了机&#xff1a; 标题 二、根因 存储空间满了。 三、解决方法 方法一 用苹果数据线&#xff08;最好是原装&#xff09;连接Mac电脑&#xff0c;在装有 macOS Catalina 10.15 或更高版本的 Mac 上&#xff0c;打开“访达”。在装有 macOS…