TensorFlow深度学习实战(19)——受限玻尔兹曼机

news2025/6/3 21:21:02

TensorFlow深度学习实战(19)——受限玻尔兹曼机

    • 0. 前言
    • 1. 受限玻尔兹曼机
      • 1.1 受限玻尔兹曼机架构
      • 1.2 受限玻尔兹曼机的数学原理
    • 2. 使用受限玻尔兹曼机重建图像
    • 3. 深度信念网络
    • 小结
    • 系列链接

0. 前言

受限玻尔兹曼机 (Restricted Boltzmann Machine, RBM) 是一种无监督学习的概率图模型,用于学习数据的特征表示。它是由两层神经元组成的网络,其中一层是可见层 (visible layer),用于表示输入数据;另一层是隐层 (hidden layer),用于捕捉数据的潜在特征,而深度信念网络是堆叠的 RBM。本节中,将介绍 RBM 的基本原理,并使用 TensorFlow 实现 RBM 和深度信念网络用于重建图像。

1. 受限玻尔兹曼机

1.1 受限玻尔兹曼机架构

受限玻尔兹曼机 (Restricted Boltzmann Machine, RBM) 是一种双层神经网络,一层称为可见层 (visible layer),另一层称为隐层 (hidden layer)。因为只有两层,因此称为浅层神经网络。RBM 最早由 Paul Smolensky1986 年提出,后来由 Geoffrey Hinton2006 年提出使用对比散度 (Contrastive Divergence, CD) 作为训练方法。所有可见层的神经元(称为可见单元,visible unit )都与隐层的所有神经元(称为隐单元,hidden unit )连接,但有一个限制——同一层中的神经元之间不能连接。RBM 中的所有神经元本质上都是二进制的,要么激活,要么不激活。
RBM 可以用于降维、特征提取和协同过滤。RBM 的训练可以分为三个部分:前向传递 (forward pass)、反向传递 (backward pass) 以及比较 (comparison)。

1.2 受限玻尔兹曼机的数学原理

接下来,我们从数学原理方面介绍 RBM,将 RBM 的操作分为两个过程:
前向传递: 可见单元 V V V 的信息通过权重 W W W 和偏置 c c c 传递到隐单元 h 0 h_0 h0。隐单元可能会激活或不激活,这取决于随机概率 σ \sigma σ,通常使用 sigmoid 函数:
ρ ( v 0 ∣ h 0 ) = σ ( V T W + c ) \rho (v_0|h_0)=\sigma(V^TW+c) ρ(v0h0)=σ(VTW+c)
反向传递: 隐单元的表示 h 0 h_0 h0 通过相同的权重 W W W,但使用不同的偏置 c c c,传递回可见单元,从而模型重建输入。同样,取决于随机概率 σ \sigma σ
ρ ( v i ∣ h 0 ) = σ ( V T h 0 + c ) \rho(v_i|h_0)=\sigma(V^Th_0+c) ρ(vih0)=σ(VTh0+c)
重复执行以上两个过程 k k k 次,或者直到收敛。根据研究, k = 1 k=1 k=1 可以得到良好的结果,因此本节将 k k k 设置为 1 1 1
可见向量 V V V 和隐向量 h h h 的联合能量表示如下:
E ( v , h ) = − b T V − c T h − V T W h E(v,h)=-b^TV-c^Th-V^TWh E(v,h)=bTVcThVTWh
每个可见向量 V V V 还关联有自由能量:
F ( v ) = − b T V − ∑ j ∈ h i d d e n l o g ( 1 + e x p ( c j + V T W ) ) F(v)=-b^TV-\sum_{j\in hidden}log(1+exp(c_j+V^TW)) F(v)=bTVjhiddenlog(1+exp(cj+VTW))
使用对比散度目标函数,即 M e a n ( F ( V o r i g i n a l ) ) − M e a n ( F ( V r e c o n s t r u c t e d ) ) Mean(F(V_{original})) - Mean(F(V_{reconstructed})) Mean(F(Voriginal))Mean(F(Vreconstructed)),权重的变化由下式表示:
d W = η [ ( V T h ) i n p u t − ( V T h ) r e c o n s t r u c t e d ] dW=\eta[(V^Th)_{input}-(V^Th)_{reconstructed}] dW=η[(VTh)input(VTh)reconstructed]
其中, η \eta η 是学习率,偏置 b b b c c c 变化的表达式与此类似。

2. 使用受限玻尔兹曼机重建图像

接下来,使用 TensorFlow 构建一个受限玻尔兹曼机 (Restricted Boltzmann Machine, RBM) 模型,用于重建 MNIST 手写数字图像。

(1) 导入 TensorFlowNumPyMatplotlib 库:

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

(2) 定义 RBM 类。RBM 类的 __init__() 函数初始化可见层的神经元数量 input_size 和隐层的神经元数量 output_size,初始化隐层和可见层的权重和偏置,本节中将它们初始化为零。也可以尝试随机初始化:

#Class that defines the behavior of the RBM
class RBM(object):
    def __init__(self, input_size, output_size, lr=1.0, batchsize=100):
        """
        m: Number of neurons in visible layer
        n: number of neurons in hidden layer
        """
        #Defining the hyperparameters
        self._input_size = input_size #Size of Visible
        self._output_size = output_size #Size of outp
        self.learning_rate = lr #The step used in gradient descent
        self.batchsize = batchsize #The size of how much data will be used for training per sub iteration
        
        #Initializing weights and biases as matrices full of zeroes
        self.w = tf.zeros([input_size, output_size], np.float32) #Creates and initializes the weights with 0
        self.hb = tf.zeros([output_size], np.float32) #Creates and initializes the hidden biases with 0
        self.vb = tf.zeros([input_size], np.float32) #Creates and initializes the visible biases with 0

定义前向和反向传递方法:

    #Forward Pass
    def prob_h_given_v(self, visible, w, hb):
        #Sigmoid 
        return tf.nn.sigmoid(tf.matmul(visible, w) + hb)

    #Backward Pass
    def prob_v_given_h(self, hidden, w, vb):
        return tf.nn.sigmoid(tf.matmul(hidden, tf.transpose(w)) + vb)

创建生成随机二进制值的函数。这是因为隐层和可见层的单元更新是使用随机概率的,这取决于每个单元的输入(对于隐层)以及从上到下的反向输入(对于可见层):

    #Generate the sample probability
    def sample_prob(self, probs):
        return tf.nn.relu(tf.sign(probs - tf.random.uniform(tf.shape(probs))))

定义函数重建输入数据:

    def rbm_reconstruct(self,X):
        h = tf.nn.sigmoid(tf.matmul(X, self.w) + self.hb)
        reconstruct = tf.nn.sigmoid(tf.matmul(h, tf.transpose(self.w)) + self.vb)
        return reconstruct

定义 train() 函数用于训练 RBM。该函数计算对比散度的正梯度和负梯度,并使用权重更新公式来更新权重和偏置:

    #Training method for the model
    def train(self, X, epochs=10):     
        loss = []
        for epoch in range(epochs):
            #For each step/batch
            for start, end in zip(range(0, len(X), self.batchsize),range(self.batchsize,len(X), self.batchsize)):
                batch = X[start:end]
                    
                #Initialize with sample probabilities
                    
                h0 = self.sample_prob(self.prob_h_given_v(batch, self.w, self.hb))
                v1 = self.sample_prob(self.prob_v_given_h(h0, self.w, self.vb))
                h1 = self.prob_h_given_v(v1, self.w, self.hb)
                    
                #Create the Gradients
                positive_grad = tf.matmul(tf.transpose(batch), h0)
                negative_grad = tf.matmul(tf.transpose(v1), h1)
                    
                #Update learning rates 
                self.w = self.w + self.learning_rate *(positive_grad - negative_grad) / tf.dtypes.cast(tf.shape(batch)[0],tf.float32)
                self.vb = self.vb +  self.learning_rate * tf.reduce_mean(batch - v1, 0)
                self.hb = self.hb +  self.learning_rate * tf.reduce_mean(h0 - h1, 0)
                    
            #Find the error rate
            err = tf.reduce_mean(tf.square(batch - v1))
            print ('Epoch: %d' % epoch,'reconstruction error: %f' % err)
            loss.append(err)
                    
        return loss

(3) 实例化一个 RBM 对象并在 MNIST 数据集上进行训练:

(train_data, _), (test_data, _) =  tf.keras.datasets.mnist.load_data()
train_data = train_data/np.float32(255)
train_data = np.reshape(train_data, (train_data.shape[0], 784))

test_data = test_data/np.float32(255)
test_data = np.reshape(test_data, (test_data.shape[0], 784))

#Size of inputs is the number of inputs in the training set
input_size = train_data.shape[1]
rbm = RBM(input_size, 200)

err = rbm.train(train_data,50)

(4) 绘制学习曲线:

plt.plot(err)
plt.xlabel('epochs')
plt.ylabel('cost')
plt.show()

训练过程监控

(5) 可视化重建图像:

out = rbm.rbm_reconstruct(test_data)

# Plotting original and reconstructed images
row, col = 2, 8
idx = np.random.randint(0, 100, row * col // 2)
f, axarr = plt.subplots(row, col, sharex=True, sharey=True, figsize=(20,4))
for fig, row in zip([test_data,out], axarr):
    for i,ax in zip(idx,row):
        ax.imshow(tf.reshape(fig[i],[28, 28]), cmap='Greys_r')
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)
plt.show()

重建图像如下所示:

重建图像

第一行是输入的手写图像,第二行是重建的图像,可以看到这些图像与人类手写的数字非常相似。

3. 深度信念网络

我们已经了解了受限玻尔兹曼机 (Restricted Boltzmann Machine, RBM),并知道如何使用对比散度进行训练,接下来,我们将介绍第一个成功的深度神经网络架构——深度信念网络 (Deep Belief Network, DBN)。DBN 架构在 2006 年由 Hinton 提出,在此模型之前,训练深度架构非常困难,不仅是由于计算资源有限,还因为梯度消失问题。在 DBN 中首次展示了如何通过贪婪的逐层训练来训练深度架构。
简单来说,DBN 只是堆叠的 RBM,每个 RBM 都使用对比散度单独训练。从训练第一个 RBM 层开始,训练完成后,训练第二个 RBM 层。第二个 RBM 的可见单元接收第一个 RBM 隐单元的输出作为输入数据。每次添加 RBM 层时重复以上过程。

(1) 堆叠 RBM 类。为了构建 DBN,需要在 RBM 类中定义一个额外的函数 rbm_output(),用于将前一 RBM 隐单元的输出传递到下一个 RBM 中:

class RBM(object):
    def __init__(self, input_size, output_size, lr=1.0, batchsize=100):
        """
        m: Number of neurons in visible layer
        n: number of neurons in hidden layer
        """
        #Defining the hyperparameters
        self._input_size = input_size #Size of Visible
        self._output_size = output_size #Size of outp
        self.learning_rate = lr #The step used in gradient descent
        self.batchsize = batchsize #The size of how much data will be used for training per sub iteration
        
        #Initializing weights and biases as matrices full of zeroes
        self.w = tf.zeros([input_size, output_size], np.float32) #Creates and initializes the weights with 0
        self.hb = tf.zeros([output_size], np.float32) #Creates and initializes the hidden biases with 0
        self.vb = tf.zeros([input_size], np.float32) #Creates and initializes the visible biases with 0

    #Forward Pass
    def prob_h_given_v(self, visible, w, hb):
        #Sigmoid 
        return tf.nn.sigmoid(tf.matmul(visible, w) + hb)

    #Backward Pass
    def prob_v_given_h(self, hidden, w, vb):
        return tf.nn.sigmoid(tf.matmul(hidden, tf.transpose(w)) + vb)
    
    #Generate the sample probability
    def sample_prob(self, probs):
        return tf.nn.relu(tf.sign(probs - tf.random.uniform(tf.shape(probs))))

    #Training method for the model
    def train(self, X, epochs=10):     
        loss = []
        for epoch in range(epochs):
            #For each step/batch
            for start, end in zip(range(0, len(X), self.batchsize),range(self.batchsize,len(X), self.batchsize)):
                batch = X[start:end]
                    
                #Initialize with sample probabilities
                    
                h0 = self.sample_prob(self.prob_h_given_v(batch, self.w, self.hb))
                v1 = self.sample_prob(self.prob_v_given_h(h0, self.w, self.vb))
                h1 = self.prob_h_given_v(v1, self.w, self.hb)
                    
                #Create the Gradients
                positive_grad = tf.matmul(tf.transpose(batch), h0)
                negative_grad = tf.matmul(tf.transpose(v1), h1)
                    
                #Update learning rates 
                self.w = self.w + self.learning_rate *(positive_grad - negative_grad) / tf.dtypes.cast(tf.shape(batch)[0],tf.float32)
                self.vb = self.vb +  self.learning_rate * tf.reduce_mean(batch - v1, 0)
                self.hb = self.hb +  self.learning_rate * tf.reduce_mean(h0 - h1, 0)
                    
            #Find the error rate
            err = tf.reduce_mean(tf.square(batch - v1))
            print ('Epoch: %d' % epoch,'reconstruction error: %f' % err)
            loss.append(err)
                    
        return loss
        
    #Create expected output for our DBN
    def rbm_output(self, X):
        out = tf.nn.sigmoid(tf.matmul(X, self.w) + self.hb)
        return out
    
    def rbm_reconstruct(self,X):
        h = tf.nn.sigmoid(tf.matmul(X, self.w) + self.hb)
        reconstruct = tf.nn.sigmoid(tf.matmul(h, tf.transpose(self.w)) + self.vb)
        return reconstruct

(2) 使用 RBM 类创建一个堆叠的 RBM 结构,第一个 RBM500 个隐单元,第二个有 200 个隐单元,第三个有 50 个隐单元:

RBM_hidden_sizes = [500, 200 , 50 ] #create 2 layers of RBM with size 400 and 100

#Since we are training, set input as training data
inpX = train_data

#Create list to hold our RBMs
rbm_list = []

#Size of inputs is the number of inputs in the training set
input_size = train_data.shape[1]

#For each RBM we want to generate
for i, size in enumerate(RBM_hidden_sizes):
    print ('RBM: ',i,' ',input_size,'->', size)
    rbm_list.append(RBM(input_size, size))
    input_size = size
"""
RBM:  0   784 -> 500
RBM:  1   500 -> 200
RBM:  2   200 -> 50
"""

(3) 对于第一个 RBMMNIST 数据作为输入。第一个 RBM 的输出作为输入传递到第二个 RBM,以此类推,通过连续的 RBM 层:

#For each RBM in our list
for rbm in rbm_list:
    print ('Next RBM:')
    #Train a new one
    rbm.train(tf.cast(inpX,tf.float32)) 
    #Return the output layer
    inpX = rbm.rbm_output(inpX)

out = rbm_list[0].rbm_reconstruct(test_data)
# Plotting original and reconstructed images
row, col = 2, 8
idx = np.random.randint(0, 100, row * col // 2)
f, axarr = plt.subplots(row, col, sharex=True, sharey=True, figsize=(20,4))
for fig, row in zip([test_data,out], axarr):
    for i,ax in zip(idx,row):
        ax.imshow(tf.reshape(fig[i],[28, 28]), cmap='Greys_r')
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)
plt.show()

本节中,这三个堆叠的受限玻尔兹曼机以无监督学习方式进行训练。DBN 还可以通过监督学习进行训练,为此,需要对训练好的 RBM 的权重进行微调,并在最后添加一个全连接层。

小结

受限玻尔兹曼机 (Restricted Boltzmann Machine, RBM) 是一种强大的无监督学习模型,可以用于特征学习、数据降维、图像生成等任务。尽管其训练过程复杂且计算开销较大,但它为深度学习的发展奠定了基础,如深度信念网络 (Deep Belief Network, DBN)。

系列链接

TensorFlow深度学习实战(1)——神经网络与模型训练过程详解
TensorFlow深度学习实战(2)——使用TensorFlow构建神经网络
TensorFlow深度学习实战(3)——深度学习中常用激活函数详解
TensorFlow深度学习实战(4)——正则化技术详解
TensorFlow深度学习实战(5)——神经网络性能优化技术详解
TensorFlow深度学习实战(6)——回归分析详解
TensorFlow深度学习实战(7)——分类任务详解
TensorFlow深度学习实战(8)——卷积神经网络
TensorFlow深度学习实战(9)——构建VGG模型实现图像分类
TensorFlow深度学习实战(10)——迁移学习详解
TensorFlow深度学习实战(11)——风格迁移详解
TensorFlow深度学习实战(12)——词嵌入技术详解
TensorFlow深度学习实战(13)——神经嵌入详解
TensorFlow深度学习实战(14)——循环神经网络详解
TensorFlow深度学习实战(15)——编码器-解码器架构
TensorFlow深度学习实战(16)——注意力机制详解
TensorFlow深度学习实战(17)——主成分分析详解
TensorFlow深度学习实战(18)——K-means 聚类详解

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

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

相关文章

告别手动绘图!基于AI的Smart Mermaid自动可视化图表工具搭建与使用指南

以下是对Smart Mermaid的简单介绍: 一款基于 AI 技术的 Web 应用程序,可将文本内容智能转换为 Mermaid 格式的代码,并将其渲染成可视化图表可以智能制作流程图、序列图、甘特图、状态图等等,并且支持在线调整、图片导出可以Docke…

【Oracle】安装单实例

个人主页:Guiat 归属专栏:Oracle 文章目录 1. 安装前的准备工作1.1 硬件和系统要求1.2 检查系统环境1.3 下载Oracle软件 2. 系统配置2.1 创建Oracle用户和组2.2 配置内核参数2.3 配置用户资源限制2.4 安装必要的软件包 3. 目录结构和环境变量3.1 创建Ora…

QT中更新或添加组件时出现“”qt操作至少需要一个处于启用状态的有效资料档案库“解决方法”

在MaintenanceTool.exe中点击下一步 第一个: 第二个: 第三个: 以上任意一个放入资料库中

论文速读《UAV-Flow Colosseo: 自然语言控制无人机系统》

论文链接:https://arxiv.org/abs/2505.15725项目主页:https://prince687028.github.io/UAV-Flow/ 0. 简介 近年来,无人机技术蓬勃发展,但如何让无人机像智能助手一样理解并执行人类语言指令,仍是一个前沿挑战。现有研…

ES6+中Promise 中错误捕捉详解——链式调用catch()或者async/await+try/catch

通过 unhandledrejection 捕捉未处理的 Promise 异常,手动将其抛出,最终让 window.onerror 捕捉,从而统一所有异常的处理逻辑 规范代码:catch(onRejected)、async...awaittry...catch 在 JavaScript 的 Pro…

解常微分方程组

Euler法 function euler_method % 参数设置 v_missile 450; % 导弹速度 km/h v_enemy 90; % 敌艇速度 km/h % 初始条件 x0 0; % 导弹初始位置 x y0 0; % 导弹初始位置 y xe0 120; % 敌艇初始位置 y t0 0; % 初始时间 % 时间步长和总时间 dt 0.01; % 时间步长 t_final …

C++实现汉诺塔游戏自动完成

目录 一、汉诺塔的规则二、数学递归推导式三、步骤实现(一)汉诺塔模型(二)递归实现(三)显示1.命令行显示2.SDL图形显示 四、处理用户输入及SDL环境配置五、总结六、源码下载 一、汉诺塔的规则 游戏由3根柱子和若干大小不一的圆盘组成,初始状态下,所有的…

pikachu靶场通关笔记07 XSS关卡03-存储型XSS

目录 一、XSS 二、存储型XSS 三、源码分析 四、渗透实战 1、输入mooyuan试一试 2、注入Payload 3、查看数据库 4、再次进入留言板页面 本系列为通过《pikachu靶场通关笔记》的XSS关卡(共10关)渗透集合,通过对XSS关卡源码的代码审计找到XSS风险的…

OpenCV CUDA模块直方图计算------用于在 GPU 上执行对比度受限的自适应直方图均衡类cv::cuda::CLAHE

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::cuda::CLAHE 是 OpenCV 的 CUDA 模块中提供的一个类,用于在 GPU 上执行对比度受限的自适应直方图均衡(Contrast Limi…

华为OD机试真题——矩形绘制(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳实现

2025 A卷 200分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…

JDBC连不上mysql:Unable to load authentication plugin ‘caching_sha2_password‘.

最近为一个spring-boot项目下了mysql-9.3.0,结果因为mysql版本太新一直报错连不上。 错误如下: 2025-06-01 16:19:43.516 ERROR 22088 --- [http-nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispat…

MacOS安装Docker Desktop并汉化

1. 安装Docker Desktop 到Docker Desktop For Mac下载对应系统的Docker Desktop 安装包,下载后安装,没有账号需要注册,然后登陆即可。 2. 汉化 前往汉化包下载链接下载对应系统的.asar文件 然后将安装好的文件覆盖原先的文件app.asar文件…

Centos系统搭建主备DNS服务

目录 一、主DNS服务器配置 1.安装 BIND 软件包 2.配置主配置文件 3.创建正向区域文件 4.创建区域数据文件 5.检查配置语法并重启服务 二、从DNS服务配置 1.安装 BIND 软件包 2.配置主配置文件 3.创建缓存目录 4.启动并设置开机自启 一、主DNS服务器配置 1.安装 BIN…

使用 HTML + JavaScript 实现在线考试系统

在现代的在线教育平台中,在线考试系统是不可或缺的一部分。本文将通过一个完整的示例,演示如何使用 HTML、CSS 和 JavaScript 构建一个支持多种题型的在线考试系统。 效果演示 项目概述 本项目主要包含以下核心功能: 支持4种常见题型&…

谷歌工作自动化——仙盟大衍灵机——仙盟创梦IDE

下载地址 https://chromewebstore.google.com/detail/selenium-ide/mooikfkahbdckldjjndioackbalphokd https://chrome.zzzmh.cn/info/mooikfkahbdckldjjndioackbalphokd

秒杀系统—1.架构设计和方案简介

大纲 1.秒杀系统的方案设计要点 2.秒杀系统的数据 页面 接口的处理方案 3.秒杀系统的负载均衡方案底层相关 4.秒杀系统的限流机制和超卖问题处理 5.秒杀系统的异步下单和高可用方案 1.秒杀系统的方案设计要点 (1)秒杀促销活动的数据处理 (2)秒杀促销活动的页面处理 (…

基于FashionMnist数据集的自监督学习(生成式自监督学习AE算法)

目录 一,生成式自监督学习 1.1 简介 1.2 核心思想 1.3 常见算法 1.3.1 自动编码器(Autoencoder) 1.3.2 生成对抗网络(GANs) 1.3.3 变分自编码器(VAE) 1.3.4 Transformer-based 模型&…

从监控到告警:Prometheus+Grafana+Alertmanager+告警通知服务全链路落地实践

文章目录 一、引言1.1 监控告警的必要性1.2 监控告警的基本原理1.2.1 指标采集与存储1.2.2 告警规则与触发机制1.2.3 多渠道通知与闭环 二、技术选型与架构设计2.1 为什么选择 Prometheus 及其生态2.1.1 Prometheus 优势分析2.1.2 Grafana 可视化能力2.1.3 Alertmanager 灵活告…

WPF【09】WPF基础入门 (三层架构与MVC架构)

9-2 【操作】WPF 基础入门 新建一项目 Create a new project - WPF Application (A project for creating a .NET Core WPF Application) - Next - .NET 5.0 (Current) - Create 项目创建完成,VS自动打开 GUI用户界面,格式是 .xaml文件,跟xm…

macOS 风格番茄计时器:设计与实现详解

macOS 风格番茄计时器:设计与实现详解 概述 本文介绍一款采用 macOS 设计语言的网页版番茄计时器实现。该计时器完全遵循苹果的人机界面指南(HIG),提供原汁原味的 macOS 使用体验,同时具备响应式设计和深色模式支持。 核心特性 原生 macOS…