【深度学习实验】注意力机制(四):点积注意力与缩放点积注意力之比较
文章目录
- 一、实验介绍
- 二、实验环境
- 1. 配置虚拟环境
- 2. 库版本介绍
- 三、实验内容
- 0. 理论介绍
- a. 认知神经学中的注意力
- b. 注意力机制
- 1. 注意力权重矩阵可视化(矩阵热图)
- 2. 掩码Softmax 操作
- 3. 打分函数——加性注意力模型
- 3. 打分函数——点积注意力与缩放点积注意力
- a. 缩放点积注意力模型
- b. 点积注意力模型
- c. 模拟实验
- d. 模型比较与选择
- e. 代码整合
一、实验介绍
注意力机制作为一种模拟人脑信息处理的关键工具,在深度学习领域中得到了广泛应用。本系列实验旨在通过理论分析和代码演示,深入了解注意力机制的原理、类型及其在模型中的实际应用。
本文将介绍将介绍带有掩码的 softmax 操作
二、实验环境
本系列实验使用了PyTorch深度学习框架,相关操作如下:
1. 配置虚拟环境
conda create -n DL python=3.7
conda activate DL
pip install torch==1.8.1+cu102 torchvision==0.9.1+cu102 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html
conda install matplotlib
conda install scikit-learn
2. 库版本介绍
| 软件包 | 本实验版本 | 目前最新版 |
|---|---|---|
| matplotlib | 3.5.3 | 3.8.0 |
| numpy | 1.21.6 | 1.26.0 |
| python | 3.7.16 | |
| scikit-learn | 0.22.1 | 1.3.0 |
| torch | 1.8.1+cu102 | 2.0.1 |
| torchaudio | 0.8.1 | 2.0.2 |
| torchvision | 0.9.1+cu102 | 0.15.2 |
三、实验内容
0. 理论介绍
a. 认知神经学中的注意力
人脑每个时刻接收的外界输入信息非常多,包括来源于视
觉、听觉、触觉的各种各样的信息。单就视觉来说,眼睛每秒钟都会发送千万比特的信息给视觉神经系统。人脑通过注意力来解决信息超载问题,注意力分为两种主要类型:
- 聚焦式注意力(Focus Attention):
- 这是一种自上而下的有意识的注意力,通常与任务相关。
- 在这种情况下,个体有目的地选择关注某些信息,而忽略其他信息。
- 在深度学习中,注意力机制可以使模型有选择地聚焦于输入的特定部分,以便更有效地进行任务,例如机器翻译、文本摘要等。
- 基于显著性的注意力(Saliency-Based Attention)
- 这是一种自下而上的无意识的注意力,通常由外界刺激驱动而不需要主动干预。
- 在这种情况下,注意力被自动吸引到与周围环境不同的刺激信息上。
- 在深度学习中,这种注意力机制可以用于识别图像中的显著物体或文本中的重要关键词。
在深度学习领域,注意力机制已被广泛应用,尤其是在自然语言处理任务中,如机器翻译、文本摘要、问答系统等。通过引入注意力机制,模型可以更灵活地处理不同位置的信息,提高对长序列的处理能力,并在处理输入时动态调整关注的重点。
b. 注意力机制
-
注意力机制(Attention Mechanism):
- 作为资源分配方案,注意力机制允许有限的计算资源集中处理更重要的信息,以应对信息超载的问题。
- 在神经网络中,它可以被看作一种机制,通过选择性地聚焦于输入中的某些部分,提高了神经网络的效率。
-
基于显著性的注意力机制的近似: 在神经网络模型中,最大汇聚(Max Pooling)和门控(Gating)机制可以被近似地看作是自下而上的基于显著性的注意力机制,这些机制允许网络自动关注输入中与周围环境不同的信息。
-
聚焦式注意力的应用: 自上而下的聚焦式注意力是一种有效的信息选择方式。在任务中,只选择与任务相关的信息,而忽略不相关的部分。例如,在阅读理解任务中,只有与问题相关的文章片段被选择用于后续的处理,减轻了神经网络的计算负担。
-
注意力的计算过程:注意力机制的计算分为两步。首先,在所有输入信息上计算注意力分布,然后根据这个分布计算输入信息的加权平均。这个计算依赖于一个查询向量(Query Vector),通过一个打分函数来计算每个输入向量和查询向量之间的相关性。
-
注意力分布(Attention Distribution):注意力分布表示在给定查询向量和输入信息的情况下,选择每个输入向量的概率分布。Softmax 函数被用于将分数转化为概率分布,其中每个分数由一个打分函数计算得到。
-
打分函数(Scoring Function):打分函数衡量查询向量与输入向量之间的相关性。文中介绍了几种常用的打分函数,包括加性模型、点积模型、缩放点积模型和双线性模型。这些模型通过可学习的参数来调整注意力的计算。
-
加性模型: s ( x , q ) = v T tanh ( W x + U q ) \mathbf{s}(\mathbf{x}, \mathbf{q}) = \mathbf{v}^T \tanh(\mathbf{W}\mathbf{x} + \mathbf{U}\mathbf{q}) s(x,q)=vTtanh(Wx+Uq)
-
点积模型: s ( x , q ) = x T q \mathbf{s}(\mathbf{x}, \mathbf{q}) = \mathbf{x}^T \mathbf{q} s(x,q)=xTq
-
缩放点积模型: s ( x , q ) = x T q D \mathbf{s}(\mathbf{x}, \mathbf{q}) = \frac{\mathbf{x}^T \mathbf{q}}{\sqrt{D}} s(x,q)=DxTq (缩小方差,增大softmax梯度)
-
双线性模型: s ( x , q ) = x T W q \mathbf{s}(\mathbf{x}, \mathbf{q}) = \mathbf{x}^T \mathbf{W} \mathbf{q} s(x,q)=xTWq (非对称性)
-
-
-
软性注意力机制:
-
定义:软性注意力机制通过一个“软性”的信息选择机制对输入信息进行汇总,允许模型以概率形式对输入的不同部分进行关注,而不是强制性地选择一个部分。
-
加权平均:软性注意力机制中的加权平均表示在给定任务相关的查询向量时,每个输入向量受关注的程度,通过注意力分布实现。
-
Softmax 操作:注意力分布通常通过 Softmax 操作计算,确保它们成为一个概率分布。
-
1. 注意力权重矩阵可视化(矩阵热图)
【深度学习实验】注意力机制(一):注意力权重矩阵可视化(矩阵热图heatmap)

2. 掩码Softmax 操作
【深度学习实验】注意力机制(二):掩码Softmax 操作

3. 打分函数——加性注意力模型
- 加性模型: s ( x , q ) = v T tanh ( W x + U q ) \mathbf{s}(\mathbf{x}, \mathbf{q}) = \mathbf{v}^T \tanh(\mathbf{W}\mathbf{x} + \mathbf{U}\mathbf{q}) s(x,q)=vTtanh(Wx+Uq)
【深度学习实验】注意力机制(三):打分函数——加性注意力模型
3. 打分函数——点积注意力与缩放点积注意力
-
点积模型: s ( x , q ) = x T q \mathbf{s}(\mathbf{x}, \mathbf{q}) = \mathbf{x}^T \mathbf{q} s(x,q)=xTq
-
缩放点积模型: s ( x , q ) = x T q D \mathbf{s}(\mathbf{x}, \mathbf{q}) = \frac{\mathbf{x}^T \mathbf{q}}{\sqrt{D}} s(x,q)=DxTq (缩小方差,增大softmax梯度)
a. 缩放点积注意力模型
class DotProductAttention(nn.Module):"""缩放点积注意力"""def __init__(self, dropout, **kwargs):super(DotProductAttention, self).__init__(**kwargs)# 使用暂退法进行模型正则化self.dropout = nn.Dropout(dropout)def forward(self, queries, keys, values, valid_lens=None):d = queries.shape[-1]self.scores = torch.bmm(queries, keys.transpose(1, 2)) / math.sqrt(d)self.attention_weights = masked_softmax(self.scores, valid_lens)return torch.bmm(self.dropout(self.attention_weights), values)
-
初始化方法 (
__init__):- 参数:
dropout: Dropout 正则化的概率。
- 说明: 初始化方法定义了模型的组件,仅包含了一个 Dropout 正则化层。
- 参数:
-
前向传播方法 (
forward):- 参数:
queries: 查询张量,形状为(batch_size, num_queries, d)。keys: 键张量,形状为(batch_size, num_kv_pairs, d)。values: 值张量,形状为(batch_size, num_kv_pairs, value_size)。valid_lens: 有效长度张量,形状为(batch_size,)或(batch_size, num_queries)。
- 返回值: 加权平均后的值张量,形状为
(batch_size, num_queries, value_size)。
- 参数:
-
实现细节:
- 计算缩放点积得分:通过张量乘法计算
queries和keys的点积,然后除以 d \sqrt{d} d 进行缩放,其中 d d d 是查询或键的维度。 - 使用
masked_softmax函数计算注意力权重,根据有效长度对注意力进行掩码。 - 将注意力权重应用到值上,得到最终的加权平均结果。
- 使用 Dropout 对注意力权重进行正则化。
- 计算缩放点积得分:通过张量乘法计算
b. 点积注意力模型
class DotProductAttention2(nn.Module):"""点积注意力"""def __init__(self, dropout, **kwargs):super(DotProductAttention2, self).__init__(**kwargs)# 使用暂退法进行模型正则化self.dropout = nn.Dropout(dropout)def forward(self, queries, keys, values, valid_lens=None):# P195:(8.3),(8.4)# 在计算得分时不进行缩放操作(即不再除以sqrt(d))self.scores = torch.bmm(queries, keys.transpose(1, 2))self.attention_weights = masked_softmax(self.scores, valid_lens)return torch.bmm(self.dropout(self.attention_weights), values)
- 区别:
- 计算点积得分:通过张量乘法计算
queries和keys的点积。
- 计算点积得分:通过张量乘法计算
c. 模拟实验
- 模型数据
queries, keys = torch.normal(0, 1, (2, 1, 2)), torch.ones((2, 10, 2))
values = torch.arange(40, dtype=torch.float32).reshape(1, 10, 4).repeat(2, 1, 1)
valid_lens = torch.tensor([2, 6])
- 模型应用
# 创建缩放点积注意力模型
attention = DotProductAttention(0.5)
attention.eval()
# 使用模型进行前向传播
attention(queries, keys, values, valid_lens)# 创建点积注意力模型
attention2 = DotProductAttention2(0.5)
attention2.eval()
# 使用模型进行前向传播
attention2(queries, keys, values, valid_lens)

- 权重可视化
为了直观地展示模型在不同输入下的注意力分布,使用前文show_heatmaps函数,通过热图的形式展示注意力权重。
# 可视化缩放点积注意力权重
show_heatmaps(attention.attention_weights.reshape((1, 1, 2, 10)),xlabel='Keys', ylabel='Queries')# 可视化点积注意力权重
show_heatmaps(attention2.attention_weights.reshape((1, 1, 2, 10)),xlabel='Keys', ylabel='Queries')

d. 模型比较与选择
-
缩放点积注意力模型:
- 适用于处理高维度的查询和键。
- 通过缩放操作有助于防止点积得分的方差过大。
-
点积注意力模型:
- 适用于处理相对较低维度的查询和键。
- 更方便地利用矩阵乘积提高计算效率。

e. 代码整合
# 导入必要的库
import math
import torch
from torch import nn
import torch.nn.functional as F
from d2l import torch as d2l
from torch.utils import datadef masked_softmax(X, valid_lens):"""通过在最后一个轴上掩蔽元素来执行softmax操作"""# X:3D张量,valid_lens:1D或2D张量if valid_lens is None:return nn.functional.softmax(X, dim=-1)else:shape = X.shapeif valid_lens.dim() == 1:valid_lens = torch.repeat_interleave(valid_lens, shape[1])else:valid_lens = valid_lens.reshape(-1)# 最后一轴上被掩蔽的元素使用一个非常大的负值替换,从而其softmax输出为0X = d2l.sequence_mask(X.reshape(-1, shape[-1]), valid_lens, value=-1e6)return nn.functional.softmax(X.reshape(shape), dim=-1)def show_heatmaps(matrices, xlabel, ylabel, titles=None, figsize=(2.5, 2.5), cmap='Reds'):"""显示矩阵热图"""d2l.use_svg_display()num_rows, num_cols = matrices.shape[0], matrices.shape[1]fig, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize,sharex=True, sharey=True, squeeze=False)for i, (row_axes, row_matrices) in enumerate(zip(axes, matrices)):for j, (ax, matrix) in enumerate(zip(row_axes, row_matrices)):pcm = ax.imshow(matrix.detach().numpy(), cmap=cmap)if i == num_rows - 1:ax.set_xlabel(xlabel)if j == 0:ax.set_ylabel(ylabel)if titles:ax.set_title(titles[j])fig.colorbar(pcm, ax=axes, shrink=0.6)class DotProductAttention(nn.Module):"""缩放点积注意力"""def __init__(self, dropout, **kwargs):super(DotProductAttention, self).__init__(**kwargs)# 使用暂退法进行模型正则化self.dropout = nn.Dropout(dropout)# queries的形状:(batch_size,查询的个数,d)# keys的形状:(batch_size,“键-值”对的个数,d)# values的形状:(batch_size,“键-值”对的个数,值的维度)# valid_lens的形状:(batch_size,)或者(batch_size,查询的个数)def forward(self, queries, keys, values, valid_lens=None):print(queries)d = queries.shape[-1]print(d)self.scores = torch.bmm(queries, keys.transpose(1, 2)) / math.sqrt(d)print(self.scores)self.attention_weights = masked_softmax(self.scores, valid_lens)return torch.bmm(self.dropout(self.attention_weights), values)class DotProductAttention2(nn.Module):"""点积注意力"""def __init__(self, dropout, **kwargs):super(DotProductAttention2, self).__init__(**kwargs)# 使用暂退法进行模型正则化self.dropout = nn.Dropout(dropout)# queries的形状:(batch_size,查询的个数,d)# keys的形状:(batch_size,“键-值”对的个数,d)# values的形状:(batch_size,“键-值”对的个数,值的维度)# valid_lens的形状:(batch_size,)或者(batch_size,查询的个数)def forward(self, queries, keys, values, valid_lens=None):# P195:(8.3),(8.4)# 在计算得分时不进行缩放操作(即不再除以sqrt(d))self.scores = torch.bmm(queries, keys.transpose(1, 2))print(self.scores)self.attention_weights = masked_softmax(self.scores, valid_lens)return torch.bmm(self.dropout(self.attention_weights), values)queries, keys = torch.normal(0, 1, (2, 1, 2)), torch.ones((2, 10, 2))
values = torch.arange(40, dtype=torch.float32).reshape(1, 10, 4).repeat(2, 1, 1)
valid_lens = torch.tensor([2, 6])# 缩放点积注意力模型
attention = DotProductAttention(0.5)
attention.eval()
attention(queries, keys, values, valid_lens)# 点积注意力模型
attention2 = DotProductAttention2(0.5)
attention2.eval()
attention2(queries, keys, values, valid_lens)
相关文章:
【深度学习实验】注意力机制(四):点积注意力与缩放点积注意力之比较
文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 理论介绍a. 认知神经学中的注意力b. 注意力机制 1. 注意力权重矩阵可视化(矩阵热图)2. 掩码Softmax 操作3. 打分函数——加性注意力模型3. 打分函数——点积注意力与缩放…...
用于图像分类任务的经典神经网络综述
🎀个人主页: https://zhangxiaoshu.blog.csdn.net 📢欢迎大家:关注🔍点赞👍评论📝收藏⭐️,如有错误敬请指正! 💕未来很长,值得我们全力奔赴更美好的生活&…...
Linux如何查找某个路径下大于1G的文件
find 命令可以用于在 Linux 或 macOS 系统中查找文件和目录。如果你想查找大于1GB的文件,可以使用 -size 选项结合 参数。以下是一个示例: find /path/to/search -type f -size 1G这里的 /path/to/search 是你要搜索的目录的路径。这个命令将查找该目录…...
Java二级医院区域HIS信息管理系统源码(SaaS服务)
一个好的HIS系统,要具有开放性,便于扩展升级,增加新的功能模块,支撑好医院的业务的拓展,而且可以反过来给医院赋能,最终向更多的患者提供更好的服务。 系统采用前后端分离架构,前端由Angular、J…...
自制编程语言(第三弹)定义Token
终于到了激动人心的实现时候了。为了实现我们的自制语言,我们需要的步骤为: 词法分析语法分析语义分析(此处不设置)解释器 详细完整的代码可以点击这里查看github项目。 词法分析: 将代码片段识别为关键词、标识符、…...
linux下的工具---yum
一、什么是yum yum是Linux下的软件包管理器 二、什么是软件包管理器 1、在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序. 2、但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成windows上的安装程序)放在…...
java全局异常处理(springboot)
介绍: 在日常项目开发中,异常是常见的,但是如何更高效的处理好异常信息,让我们能快速定位到BUG,是很重要的,不仅能够提高我们的开发效率,还能让你代码看上去更舒服,SpringBoot的项目…...
JAVA将PDF转图片
前言 当今时代,PDF 文件已经成为了常用的文档格式。然而,在某些情况下,我们可能需要将 PDF 文件转换为图片格式,以便更方便地分享和使用。这时,我们可以使用 Java 编程语言来实现这个功能。Java 提供了许多库和工具&a…...
合并区间[中等]
一、题目 以数组intervals表示若干个区间的集合,其中单个区间为intervals[i] [starti, endi]。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。 示例 1: 输入:intervals […...
MYSQL基础知识之【LIKE子句的使用 ,NULL值的处理,空值的处理】
文章目录 前言MySQL LIKE 子句在PHP脚本中使用 LIKE 子句 MySQL NULL 值处理在命令提示符中使用 NULL 值使用PHP脚本处理 NULL 值 后言 前言 hello world欢迎来到前端的新世界 😜当前文章系列专栏:Mysql 🐱👓博主在前端领域还有…...
线索二叉树:C++实现
引言: 线索二叉树是一种特殊的二叉树,它可以通过线索(线索是指在二叉树中将空指针改为指向前驱或后继的指针)的方式将二叉树转化为一个线性结构,从而方便对二叉树进行遍历。本文将介绍如何使用C实现线索二叉树。 技术…...
C++——vector互换容器与预留空间
一.vector互换容器 功能描述:实现两个容器内元进行互换 函数原型: swap(vec); //将vec与本身的元素互换 实例: //1.基本使用 void test01() {vector<int>v1;for (int i 0; i < 10; i){v1.push_back(i);}cout << "交换前:" << e…...
Unity 自带的一些可以操控时间的属性或方法。
今天来总结下Unity自带的一些可以操控时间的方法。 1、Time.time。比较常用计算运行时间而触发特定事件。 public class Controller : MonoBehaviour {public float eventTime 5f; // 触发事件的时间private float startTime; // 游戏开始的时间private void Start(){startT…...
vue 项目中使用 mqtt
1、在html 中用cdn方式引入 <script src"https://unpkg.com/mqtt/dist/mqtt.min.js"></script> 2、封装代码 mqtt_connect.js // import * as mqtt from mqtt/dist/mqtt.min // 不知道为什么 我用引入的方式不成,就在html 用的cdn方式接入了…...
linux shell操作 - 05 进程 与 IO 模型
文章目录 计算机内存分配进程与子进程流IO模型非阻塞IOIO多路复用网络IO模型简单的socket并发的socket 计算机内存分配 一个32位,4G内存的计算机,内存使用分为两部分: 操作系统内核空间;应用程序的用户空间使用的操作系统不同&a…...
让SOME/IP运转起来——SOME/IP系统设计(下)之数据库开发
上一篇我们介绍了SOME/IP矩阵的设计流程,这一篇重点介绍如何把SOME/IP矩阵顺利的交给下游软件团队进行开发。 车载以太网通信矩阵开发完成后,下一步应该做什么? 当我们完成SOME/IP矩阵开发,下一步需要把开发完成的矩阵换成固定格…...
Mybatis反射工厂类DefaultReflectorFactory
DefaultReflectorFactory是反射工厂接口ReflectorFactory的默认实现,其主要是实现了对反射对象Reflector的创建和缓存。 有三个方法: // 判断是否开启缓存boolean isClassCacheEnabled();// 设置是否缓存void setClassCacheEnabled(boolean classCacheEn…...
antDesignPro a-table样式二次封装
antDesignPro是跟element-ui类似的一个样式框架,其本身就是一个完整的后台系统,风格样式都很统一。我使用的是antd pro vue,版本是1.7.8。公司要求使用这个框架,但是UI又有自己的一套设计。这就导致我需要对部分组件进行一定的个性…...
找免费4K高清图片素材,就上这6个网站
使用图片素材怕侵权?那就上这6个网站,免费下载,4K高清无水印,赶紧收藏起来~ 1、菜鸟图库 https://www.sucai999.com/pic.html?vNTYxMjky 一个很大的素材库,站内主要还是以设计素材为主,像图片素材就有上百…...
代码随想录算法训练营第35天| 860.柠檬水找零 406.根据身高重建队列 452. 用最少数量的箭引爆气球
JAVA代码编写 860.柠檬水找零 在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。 每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须…...
Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
