使用 TensorFlow 创建生产级机器学习模型(基于数据流编程的符号数学系统)——学习笔记

 资源出处:初学者的 TensorFlow 2.0 教程  |  TensorFlow Core (google.cn)


前言

对于新框架的学习,阅读官方文档是一种非常有效的方法。官方文档通常提供了关于框架的详细信息、使用方法和示例代码,可以帮助你快速了解和掌握框架的使用。

如果你提到的Jupyter笔记本格式的教程无法在国内运行,你可以尝试其他方式来学习框架。以下是一些建议:

一、针对新手的demo

初学者的 TensorFlow 2.0 教程  |  TensorFlow Corehttps://tensorflow.google.cn/tutorials/quickstart/beginner?hl=zh_cn1. 下载并安装 TensorFlow 2.0 测试版包。将 TensorFlow 载入你的程序:

# 安装 TensorFlowimport tensorflow as tf

2. 载入并准备好 Keras自带的MNIST 数据集。将样本从整数转换为浮点数:

mnist = tf.keras.datasets.mnist(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

3. 将模型的各层堆叠起来,以搭建 tf.keras.Sequential 模型。为训练选择优化器和损失函数:

model = tf.keras.models.Sequential([  # 定义模型网络结构tf.keras.layers.Flatten(input_shape=(28, 28)),tf.keras.layers.Dense(128, activation='relu'),tf.keras.layers.Dropout(0.2),tf.keras.layers.Dense(10, activation='softmax')
])model.compile(optimizer='adam',  # 定义模型优化器loss='sparse_categorical_crossentropy',  # 定义模型loss函数metrics=['accuracy'])  # 定义模型的指标

4. 训练并验证模型:

model.fit(x_train, y_train, epochs=5)  # 5轮model.evaluate(x_test,  y_test, verbose=2)

5. 训练结果:

现在,这个照片分类器的准确度已经达到 0.9778!!!

二、进阶demo

针对专业人员的 TensorFlow 2.0 入门  |  TensorFlow Corehttps://tensorflow.google.cn/tutorials/quickstart/advanced?hl=zh_cn

1. 注意在TensorFlow中,tensor的通道排序和pytorch有所不同。

       1)TensorFlow tensor: [batch, height, width, channel]

        2)Pytorch tensor:  [batch,channel, height, width]

2. 这个demo分为model.py和train.py两个脚本。

3.使用mnist数据集,是一个识别人手写的数据集。

        大概就是这样:

2.1 model.py


1. 模型的搭建风格

        使用Model Subclassing API,类似于pytorch的一种风格。

2. 先看一下import

        我们自己构造的MyModel继承了keras的Model类

        搭建网络用的Dense、Flatten、Conv2D函数来自keras的layers类

from tensorflow.keras.layers import Dense, Flatten, Conv2D
from tensorflow.keras import Model
3. 搭建 
class MyModel(Model):def __init__(self):super(MyModel, self).__init__() self.conv1 = Conv2D(32, 3, activation='relu')  # 卷积层self.flatten = Flatten()  # 展平self.d1 = Dense(128, activation='relu')  # 全连接层self.d2 = Dense(10)  # 全连接层# 定义网络正向传播的过程def call(self, x):  x = self.conv1(x)      # input[batch,28,28,1] ouput[batch,26,26,32] x = self.flatten(x)    # output[batch,21632]x = self.d1(x)         # output[batch,128]return self.d2(x)      # output[batch,10]

 1)super函数:避免多继承带来的问题 

                2)Conv2D函数,我们来看一下它的参数

这里要注意padding的使用方式和pytorch不同。padding参数由两种值“valid”和“same”。默认“valid”。valid表示不需要补0,same表示需要补0.。输出图片大小的计算公式如下:

3)Faltten函数:展平。因为要和后面的全连接层进行连接,所以要展成一维向量的形式。

        4)Dense函数的参数,比较重要的就是前两个参数 。还有一点需要注意,最后一个全连接层的节点数等于我们的分类数。因为mnist数据集的分类数是10,所以,规定最后一个全连接层的节点数为10。并且最后一个全连接层跟着的激活函数为“softmax”,最终softmax给出的就是属于每个类别的概率分布了。

 5)和pytorch的另一个区别:上面用到的Conv2D、Dense、Faltten函数的参数中没有指定input是什么。因为TensorFlow会自动进行推理,这也是TensorFlow的强大之处。

        6)再来看张量在这个网络中的变化情况。首先,输入进去的图片大小是28×28×1的,经过卷积层后,用上面的padding公式去计算,输出是26×26×32(通道数32是我们指定的)的;然后经过展平操作,batch数不变,图片变成1维向量,26×26×32=21632个值;输入到结点个数为128的全连接层,得到128个值的一维向量;最后,输入到结点个数为10的全连接层,得到10个值的一维向量。

2.2 train.py

2.2 tensorflow2官方demo_哔哩哔哩_bilibiliTensorflow2官方demo,手写数字的识别, 视频播放量 57662、弹幕量 151、点赞数 661、投硬币枚数 619、收藏人数 352、转发人数 91, 视频作者 霹雳吧啦Wz, 作者简介 学习学习。。。,相关视频:深度学习在图像处理中的应用(tensorflow、pytorch分别实现),PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】,5.2 使用pytorch搭建GoogLeNet网络,4.2 使用pytorch搭建VGG网络,在Pytorch中使用Tensorboard可视化训练过程,6.1.2 ResNeXt网络结构,5.1 GoogLeNet网络详解,[双语字幕]吴恩达深度学习deeplearning.ai,1.2 卷积神经网络基础补充,15.1 MobileViT网络讲解https://www.bilibili.com/video/BV1n7411T7o6?spm_id_from=333.999.0.0模型的搭建步骤遵循这篇博客:Tensorflow1.0 和 Tensorflow2.0之间的区别-CSDN博客https://blog.csdn.net/Zhiyilang/article/details/134923567?spm=1001.2014.3001.5501

2.2.1 准备输入数据

        1. 导入输入模块和数据集

        一共有60000张灰度图。

import tensorflow as tf
from model import MyModel# ketas自带的数据集
mnist = tf.keras.datasets.mnist# 下载数据集
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

默认下载到了C盘用户文件夹下的.keras目录下。

        

2. 加入通道数这一维度       

# Add a channels dimension
x_train = x_train[..., tf.newaxis].astype("float32")
x_test = x_test[..., tf.newaxis].astype("float32")

从mnist导入的数据集是28×28的灰度图,通过下图加断点的方式可以看到:

        

     增加一个维度后,我们再通过加断点的方式来查看一下,数据集变成了28×28×1的格式

3. 构造数据生成器

        构造训练集和测试集的数据生成器:

train_ds = tf.data.Dataset.from_tensor_slices((x_train, y_train)).shuffle(10000).batch(32)test_ds = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)

1)调用TensorFlow的from_tensor_slices函数:把数据x_train和标签y_train合并为元祖的方式作为输入。

2)shuffle函数:每次从硬盘中导入10000张图片到内存(训练集一共有60000张),进行随机洗牌打乱顺序,保证数据的随机性。理论上来说,括号内的取值越接近训练集的总数越贴近随机采样(越好),但是内存不允许。

 3)batch函数:32张图作为一个batch。

2.2.2  搭建模型
        实例化一个我们的模型

# Create an instance of the model
model = MyModel()

2.2.3 定义损失函数及优化器

loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)optimizer = tf.keras.optimizers.Adam()
2.2.4 传参计算model()

        1. 定义历史平均loss和历史平均精确率

        他们在 每个epoch上累计值,然后打印出一个epoch累计的结果。便于一轮一轮地观察训练情况

train_loss = tf.keras.metrics.Mean(name='train_loss')
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')test_loss = tf.keras.metrics.Mean(name='test_loss')
test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy')

2. 训练模型函数

@tf.function
def train_step(images, labels):with tf.GradientTape() as tape:# training=True is only needed if there are layers with different# behavior during training versus inference (e.g. Dropout).predictions = model(images, training=True)loss = loss_object(labels, predictions)  # 一张图的loss# 得到误差梯度(将loss反向传播到model.trainable_variables模型每一个可训练的参数中gradients = tape.gradient(loss, model.trainable_variables)  # 使用误差梯度更新模型参数optimizer.apply_gradients(zip(gradients, model.trainable_variables))train_loss(loss)  # 历史平均loss,全局变量(再次进入函数后在上一次结果的基础上变)train_accuracy(labels, predictions)  # 历史平均准确率

1)tf.GradientTape:与pytorch不同,pytorch会自动跟踪每一个训练参数的误差梯度,而TensorFlow不会。 在这里使用tf.GradientTape函数去记,返回tape。

        2)tape.gradient函数:将loss反向传播到模型每一个可训练的参数中(model.trainable_variables),最终得到误差梯度

        3)优化器Adam的apply_gradients函数:将误差梯度和模型的所有可训练参数用Zip函数打包成元祖,作为函数的输入。使用误差梯度更新模型参数。

        4)最后两行是用来计算历史平均loss和历史平均准确率。根据后面将要讲的“清零”代码,可以看出,计算的是一个epoch的历史平均loss和历史平均准确率。

        5)tf.function修饰器:用来将修饰的函数转化成图结构,那么以后这个函数内就不能加断点了,但是会大大提升训练的效率。

        3. 测试模型函数
 

@tf.function
def test_step(images, labels):# training=False is only needed if there are layers with different# behavior during training versus inference (e.g. Dropout).predictions = model(images, training=False)t_loss = loss_object(labels, predictions)test_loss(t_loss)test_accuracy(labels, predictions)

    4. 开始跑训练及测试

EPOCHS = 5for epoch in range(EPOCHS):# Reset the metrics at the start of the next epochtrain_loss.reset_states()train_accuracy.reset_states()test_loss.reset_states()test_accuracy.reset_states()# 从训练数据生成器中取数据,训练for images, labels in train_ds:train_step(images, labels)for test_images, test_labels in test_ds:test_step(test_images, test_labels)print(f'Epoch {epoch + 1}, 'f'Loss: {train_loss.result()}, 'f'Accuracy: {train_accuracy.result() * 100}, 'f'Test Loss: {test_loss.result()}, 'f'Test Accuracy: {test_accuracy.result() * 100}')

  1)reset_states函数:每一轮都把tf.keras.metrics(训练loss、训练准确率、测试loss、测试准确率)清零。这样才不影响下一轮的训练。

        2)result函数:获取tf.keras.metrics(训练loss、训练准确率、测试loss、测试准确率)的结果

2.3 运行结果

Epoch 1, Loss: 0.13267311453819275, Accuracy: 96.02333068847656, Test Loss: 0.061965566128492355, Test Accuracy: 98.06999969482422
Epoch 2, Loss: 0.042059458792209625, Accuracy: 98.72000122070312, Test Loss: 0.048135414719581604, Test Accuracy: 98.3499984741211
Epoch 3, Loss: 0.023382706567645073, Accuracy: 99.24333190917969, Test Loss: 0.05048979073762894, Test Accuracy: 98.3499984741211
Epoch 4, Loss: 0.01350458711385727, Accuracy: 99.57333374023438, Test Loss: 0.0620846264064312, Test Accuracy: 98.25
Epoch 5, Loss: 0.009131859056651592, Accuracy: 99.69999694824219, Test Loss: 0.06238056719303131, Test Accuracy: 98.33999633789062

官网原文:(仅供参考)

1、在 Google Colab 中运行https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/zh-cn/tutorials/quickstart/beginner.ipynb?hl=zh-cn

在 GitHub 查看源代码https://github.com/tensorflow/docs-l10n/blob/master/site/zh-cn/tutorials/quickstart/beginner.ipynb

此简短介

此简短介绍使用 Keras 进行以下操作:

  1. 加载一个预构建的数据集。
  2. 构建对图像进行分类的神经网络机器学习模型。
  3. 训练此神经网络。
  4. 评估模型的准确率。

这是一个 Google Colaboratory 笔记本文件。 Python程序可以直接在浏览器中运行,这是学习 Tensorflow 的绝佳方式。想要学习该教程,请点击此页面顶部的按钮,在Google Colab中运行笔记本。

  1. 在 Colab中, 连接到Python运行环境: 在菜单条的右上方, 选择 CONNECT
  2. 运行所有的代码块: 选择 Runtime > Run all

设置 TensorFlow

首先将 TensorFlow 导入到您的程序:

import tensorflow as tf
2023-11-01 06:25:27.904924: E tensorflow/stream_executor/cuda/cuda_blas.cc:2981] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered
2023-11-01 06:25:28.630454: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvrtc.so.11.1: cannot open shared object file: No such file or directory
2023-11-01 06:25:28.630689: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvrtc.so.11.1: cannot open shared object file: No such file or directory
2023-11-01 06:25:28.630701: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.

如果您在自己的开发环境而不是 Colab 中操作,请参阅设置 TensorFlow 以进行开发的安装指南。

注:如果您使用自己的开发环境,请确保您已升级到最新的 pip 以安装 TensorFlow 2 软件包。有关详情,请参阅安装指南。

加载数据集

加载并准备 MNIST 数据集。将样本数据从整数转换为浮点数:

mnist = tf.keras.datasets.mnist(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

构建机器学习模型

通过堆叠层来构建 tf.keras.Sequential 模型。

model = tf.keras.models.Sequential([tf.keras.layers.Flatten(input_shape=(28, 28)),tf.keras.layers.Dense(128, activation='relu'),tf.keras.layers.Dropout(0.2),tf.keras.layers.Dense(10)
])

对于每个样本,模型都会返回一个包含 logits 或 log-odds 分数的向量,每个类一个。

predictions = model(x_train[:1]).numpy()
predictions
array([[-0.5549733 , -0.178904  ,  0.2521443 , -0.20637012,  0.24012126,-0.09939454, -0.43023387,  0.45244563,  0.37959167, -0.43027684]],dtype=float32)

tf.nn.softmax 函数将这些 logits 转换为每个类的概率

tf.nn.softmax(predictions).numpy()

array([[0.0572833 , 0.08343588, 0.12839696, 0.0811754 , 0.12686247,0.09034067, 0.06489357, 0.15687165, 0.1458493 , 0.06489078]],dtype=float32)

注:可以将 tf.nn.softmax 烘焙到网络最后一层的激活函数中。虽然这可以使模型输出更易解释,但不建议使用这种方式,因为在使用 softmax 输出时不可能为所有模型提供精确且数值稳定的损失计算。

使用 losses.SparseCategoricalCrossentropy 为训练定义损失函数,它会接受 logits 向量和 True 索引,并为每个样本返回一个标量损失。

loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

此损失等于 true 类的负对数概率:如果模型确定类正确,则损失为零。

这个未经训练的模型给出的概率接近随机(每个类为 1/10),因此初始损失应该接近 -tf.math.log(1/10) ~= 2.3

loss_fn(y_train[:1], predictions).numpy()

2.4041677

在开始训练之前,使用 Keras Model.compile 配置和编译模型。将 optimizer 类设置为 adam,将 loss 设置为您之前定义的 loss_fn 函数,并通过将 metrics 参数设置为 accuracy 来指定要为模型评估的指标。

model.compile(optimizer='adam',loss=loss_fn,metrics=['accuracy'])

训练并评估模型

使用 Model.fit 方法调整您的模型参数并最小化损失:

model.fit(x_train, y_train, epochs=5)

Epoch 1/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.2982 - accuracy: 0.9140
Epoch 2/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.1422 - accuracy: 0.9573
Epoch 3/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.1080 - accuracy: 0.9668
Epoch 4/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0857 - accuracy: 0.9732
Epoch 5/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0754 - accuracy: 0.9759
<keras.callbacks.History at 0x7fcad46da820>

Model.evaluate 方法通常在 "Validation-set" 或 "Test-set" 上检查模型性能。

model.evaluate(x_test,  y_test, verbose=2)

313/313 - 1s - loss: 0.0674 - accuracy: 0.9791 - 588ms/epoch - 2ms/step
[0.06739047914743423, 0.9790999889373779]

现在,这个照片分类器的准确度已经达到 98%。想要了解更多,请阅读 TensorFlow 教程。

如果您想让模型返回概率,可以封装经过训练的模型,并将 softmax 附加到该模型:

probability_model = tf.keras.Sequential([model,tf.keras.layers.Softmax()
])

probability_model(x_test[:5])

<tf.Tensor: shape=(5, 10), dtype=float32, numpy=
array([[5.22209795e-08, 2.41216696e-07, 1.94858385e-05, 6.03494642e-04,3.63586158e-12, 1.27487894e-07, 2.75295411e-12, 9.99375284e-01,3.34941291e-07, 1.01254307e-06],[1.27781030e-09, 5.08851197e-04, 9.99486089e-01, 1.27736268e-06,1.16675265e-17, 2.87630655e-06, 3.99693533e-07, 1.31204751e-14,4.28570189e-07, 8.51850329e-13],[8.57534303e-07, 9.99213696e-01, 5.82170615e-05, 2.93258381e-06,2.00496142e-05, 8.48563104e-06, 1.34474585e-05, 5.36364911e-04,1.44018544e-04, 2.03336299e-06],[9.97936487e-01, 3.06930929e-07, 9.99198644e-04, 2.78684138e-06,4.31515036e-06, 3.91961257e-05, 8.49796401e-04, 1.12621237e-04,4.11850197e-05, 1.40180309e-05],[4.24850878e-05, 1.55146904e-06, 2.20226084e-05, 7.31444686e-07,9.98439252e-01, 7.06452113e-07, 8.66830524e-05, 1.98422102e-04,2.75226976e-05, 1.18070992e-03]], dtype=float32)>

结论

恭喜!您已经利用 Keras API 借助预构建数据集训练了一个机器学习模型。

有关使用 Keras 的更多示例,请查阅教程。要详细了解如何使用 Keras 构建模型,请阅读指南。如果您想详细了解如何加载和准备数据,请参阅有关图像数据加载或 CSV 数据加载的教程。

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

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

相关文章

MFC逆向之CrackMe Level3 过反调试 + 写注册机

今天我来分享一下,过反调试的方法以及使用IDA还原代码 写注册机的过程 由于内容太多,我准备分为两个帖子写,这个帖子主要是写IDA还原代码,下一个帖子是写反调试的分析以及过反调试和异常 这个CrackMe Level3是一个朋友发我的,我也不知道他在哪里弄的,我感觉挺好玩的,对反调试…

DS冲刺整理做题定理(四)查找与排序

最后一期更新&#xff0c;考试之前应该不会再出该专题了&#xff0c;之后有时间会出一些有关链表的代码题&#xff0c;其他章节只挑选重点的总结~ 一.查找 1.顺序查找 又被称为线性查找&#xff0c;对顺序表和链表都使用~基本思想是从某一端开始&#xff0c;逐个检查关键字是否…

VR云游打造沉浸式文旅新体验,延伸智慧文旅新业态

从“跃然纸上”到“映入眼帘”&#xff0c;随着国家数字化战略的深入实施&#xff0c;文旅产业的数字化转型正在不断加快&#xff0c;“沉浸式”逐渐成为了文旅消费新热点。VR技术与文旅产业相融合&#xff0c;新产品、新模式、新业态不断涌现&#xff0c;文旅资源逐渐“活”起…

智能插座是什么

智能插座 电工电气百科 文章目录 智能插座前言一、智能插座是什么二、智能插座的类别三、智能插座的原理总结 前言 智能插座的应用广泛&#xff0c;可以用于智能家居系统中的电器控制&#xff0c;也可以应用在办公室、商业场所和工业控制中&#xff0c;方便快捷地实现电器的远…

Milesight VPN server.js 任意文件读取漏洞(CVE-2023-23907)

0x01 产品简介 MilesightVPN 是一款软件&#xff0c;一个 Milesight 产品的 VPN 通道设置过程更加完善&#xff0c;并可通过网络服务器界面连接状态。 0x02 漏洞概述 MilesightVPN server.js接口处存在文件读取漏洞&#xff0c;攻击者可通过该漏洞读取系统重要文件&#xff…

西南交通大学【数电实验7---按键防抖动设计】

实验电路图、状态图、程序代码、仿真代码、仿真波形图&#xff08;可以只写出核心功能代码&#xff0c;代码要有注释&#xff09; 一共四个状态&#xff1a;1.未按下时空闲状态 2.按下抖动滤除状态 3.按下稳定状态 4.释放抖动滤除状态 在第一个状态时&#xff0c;等待按键按下&…

Nginx七层代理,四层代理 + Tomcat多实例部署

目录 1.tomcat多实例部署 准备两台虚拟机 进入pc1 pc2同时安装jdk 进入pc1 pc2安装tomcat PC1配置&#xff08;192.168.88.50&#xff09; 安装tomcat多实例 tomcat2中修改端口 启动tomcat1 tomcat2 分别在三个tomcat服务上部署jsp的动态页面 2.nginx的七层代理&…

使用set和emit在uni-app中实现响应式属性和自定义事件

在uni-app中&#xff0c;我们经常需要动态设置响应式属性&#xff0c;并且通过自定义事件来实现组件间的通信。这时&#xff0c;我们可以使用set和emit来轻松实现这些功能。 使用$set动态设置响应式属性 在Vue中&#xff0c;我们可以使用来动态设置响应式属性。在uniapp中使用…

Linux部署MySQL5.7和8.0版本 | CentOS和Ubuntu系统详细步骤安装

一、MySQL数据库管理系统安装部署【简单】 简介 MySQL数据库管理系统&#xff08;后续简称MySQL&#xff09;&#xff0c;是一款知名的数据库系统&#xff0c;其特点是&#xff1a;轻量、简单、功能丰富。 MySQL数据库可谓是软件行业的明星产品&#xff0c;无论是后端开发、…

Redis设计与实现之跳跃表

目录 一、跳跃表 1、跳跃表的实现 2、跳跃表的应用 3、跳跃表的时间复杂度是什么&#xff1f; 二、跳跃表有哪些应用场景&#xff1f; 三、跳跃表和其他数据结构&#xff08;如数组、链表等&#xff09;相比有什么优点和缺点&#xff1f; 四、Redis的跳跃表支持并发操作吗…

LeetCode:2415. 反转二叉树的奇数层(层次遍历 Java)

目录 2415. 反转二叉树的奇数层 题目描述&#xff1a; 实现代码与解析&#xff1a; BFS 原理思路&#xff1a; 2415. 反转二叉树的奇数层 题目描述&#xff1a; 给你一棵 完美 二叉树的根节点 root &#xff0c;请你反转这棵树中每个 奇数 层的节点值。 例如&#xff0c;…

【单调栈]LeetCode84: 柱状图中最大的矩形

作者推荐 【动态规划】【广度优先搜索】LeetCode:2617 网格图中最少访问的格子数 本文涉及的知识点 单调栈 题目 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形…