深度学习 精选笔记(4)线性神经网络-交叉熵回归与Softmax 回归

学习参考:

  • 动手学深度学习2.0
  • Deep-Learning-with-TensorFlow-book
  • pytorchlightning

①如有冒犯、请联系侵删。
②已写完的笔记文章会不定时一直修订修改(删、改、增),以达到集多方教程的精华于一文的目的。
③非常推荐上面(学习参考)的前两个教程,在网上是开源免费的,写的很棒,不管是开始学还是复习巩固都很不错的。

深度学习回顾,专栏内容来源多个书籍笔记、在线笔记、以及自己的感想、想法,佛系更新。争取内容全面而不失重点。完结时间到了也会一直更新下去,已写完的笔记文章会不定时一直修订修改(删、改、增),以达到集多方教程的精华于一文的目的。所有文章涉及的教程都会写在开头、一起学习一起进步。

一、交叉熵回归

交叉熵回归(Cross-Entropy Regression):交叉熵是一种用于衡量两个概率分布之间差异的指标,在机器学习中常用于分类问题的损失函数。交叉熵回归通常用于二分类问题,其中模型输出的是一个概率值,表示样本属于某一类的概率,损失函数是交叉熵损失函数。交叉熵损失函数可以表示为:
在这里插入图片描述

二、Softmax 回归

Softmax 回归:Softmax 回归是一种多分类模型,用于将模型的输出转化为多个类别的概率分布。在 Softmax 回归中,模型的最后一层是一个 Softmax 函数,将模型的输出转化为每个类别的概率,使得所有类别的概率之和为 1。
在这里插入图片描述

交叉熵回归通常用于二分类问题,而 Softmax 回归用于多分类问题。此外,Softmax 回归中的 Softmax 函数将模型输出转化为概率分布,而交叉熵回归中的损失函数用于衡量二分类模型输出与真实标签的差异。

1.分类问题

分类任务是机器学习和统计学中常见的一类任务,其目标是将数据集中的样本划分到预定义的类别或标签中。分类任务通常涉及预测离散的输出值,即将样本分为不同的类别。例如,将电子邮件分为“垃圾邮件”和“非垃圾邮件”、将图片识别为“猫”、“狗”或“鸟”等。

分类问题是指解决这类分类任务的问题。分类问题的关键在于构建一个分类模型,该模型可以根据输入的特征将样本正确地分配到各个类别中。常用的分类算法包括逻辑回归、支持向量机(SVM)、决策树、随机森林、K近邻(K-NN)等。

一种表示分类数据的简单方法:独热编码(one-hot encoding)。 独热编码是一个向量,它的分量和类别一样多。 类别对应的分量设置为1,其他所有分量设置为0。 从一个图像分类问题开始,假设每个图像属于类别“猫”“鸡”和“狗”中的一个、每次输入是一个 2×2 的灰度图像、每个图像对应四个特征𝑥1,𝑥2,𝑥3,𝑥4。标签 𝑦将是一个三维向量, 其中 (1,0,0) 对应于“猫”、 (0,1,0) 对应于“鸡”、 (0,0,1) 对应于“狗”。
在这里插入图片描述

2.网络结构

为了估计所有可能类别的条件概率,需要一个有多个输出的模型,每个类别对应一个输出。 为了解决线性模型的分类问题,需要和输出一样多的仿射函数(affine function)。 每个输出对应于它自己的仿射函数。

在识别猫、鸡、狗的任务中,为每个输入计算三个未规范化的预测(logit):𝑜1、𝑜2和𝑜3:
在这里插入图片描述
与线性回归一样,softmax回归也是一个单层神经网络。 由于计算每个输出 𝑜1 、 𝑜2 和 𝑜3取决于 所有输入 𝑥1 、 𝑥2 、 𝑥3 和 𝑥4 , 所以softmax回归的输出层也是全连接层。
在这里插入图片描述
通过向量形式表达为𝐨=𝐖𝐱+𝐛, 这是一种更适合数学和编写代码的形式。 由此,已经将所有权重放到一个3×4矩阵中。 对于给定数据样本的特征𝐱, 输出是由权重与输入特征进行矩阵-向量乘法再加上偏置𝐛得到的。

3.softmax运算

将优化参数以最大化观测数据的概率。 为了得到预测结果,将设置一个阈值,如选择具有最大概率的标签。

softmax函数能够将未规范化的预测变换为非负数并且总和为1,同时让模型保持 可导的性质。 为了完成这一目标,我们首先对每个未规范化的预测求幂,这样可以确保输出非负。 为了确保最终输出的概率值总和为1,我们再让每个求幂后的结果除以它们的总和。如下式:
在这里插入图片描述
因此, 𝐲̂ 可以视为一个正确的概率分布。 softmax运算不会改变未规范化的预测 𝐨之间的大小次序,只会确定分配给每个类别的概率。 因此,在预测过程中,仍然可以用下式来选择最有可能的类别。

在这里插入图片描述
尽管softmax是一个非线性函数,但softmax回归的输出仍然由输入特征的仿射变换决定。 因此,softmax回归是一个线性模型(linear model)。

4.小批量样本的矢量化

为了提高计算效率并且充分利用GPU,通常会对小批量样本的数据执行矢量计算。 softmax回归的矢量计算表达式为:
在这里插入图片描述
相对于一次处理一个样本, 小批量样本的矢量化加快了 𝐗和𝐖的矩阵-向量乘法。 由于 𝐗中的每一行代表一个数据样本, 那么softmax运算可以按行(rowwise)执行: 对于 𝐎 的每一行,先对所有项进行幂运算,然后通过求和对它们进行标准化。

5.损失函数

需要一个损失函数来度量预测的效果。 将使用最大似然估计,这与在线性回归中相同。

(1)对数似然

softmax函数给出了一个向量 𝐲̂ , 可以将其视为“对给定任意输入 𝐱的每个类的条件概率”。 例如, 𝑦̂ 1 = 𝑃(𝑦=猫∣𝐱)。 假设整个数据集 {𝐗,𝐘}具有 𝑛个样本, 其中索引 𝑖的样本由特征向量 𝐱(𝑖)和独热标签向量 𝐲(𝑖)组成。 可以将估计值与实际值进行比较:
在这里插入图片描述
根据最大似然估计,最大化 𝑃(𝐘∣𝐗) ,相当于最小化负对数似然:
在这里插入图片描述
其中,对于任何标签 𝐲和模型预测 𝐲̂ ,损失函数为:
在这里插入图片描述
上述损失函数 通常被称为交叉熵损失公式(cross-entropy loss)

(2)softmax及其导数

由于softmax和相关的损失函数很常见, 因此需要更好地理解它的计算方式。将𝐲̂ =softmax(𝐨) 代入交叉熵损失公式中,得到:
在这里插入图片描述
考虑相对于任何未规范化的预测 𝑜𝑗 的导数,得到:
在这里插入图片描述
上述最终导数表示,导数是softmax模型分配的概率与实际发生的情况(由独热标签向量表示)之间的差异。

(3)交叉熵损失

定义损失 𝑙, 它是所有标签分布的预期损失值。 此损失称为交叉熵损失(cross-entropy loss),它是分类问题最常用的损失之一。
在这里插入图片描述

用一个概率向量表示来阐述整个结果的分布情况,如 (0.1,0.2,0.7), 而不是仅包含二元项的向量 (0,0,1) ,,即观察到的不仅仅是一个结果。

6.模型预测和评估

在训练softmax回归模型后,给出任何样本特征,便可以预测每个输出类别的概率。 通常使用预测概率最高的类别作为输出类别。 如果预测与实际类别(标签)一致,则预测是正确的。最后使用精度(accuracy)来评估模型的性能。 精度等于正确预测数与预测总数之间的比率。

三、Fashion-MNIST图片分类数据集

1.加载数据集

通过框架中的内置函数将Fashion-MNIST数据集下载并读取到内存中。Fashion-MNIST由10个类别的图像组成, 每个类别由训练数据集(train dataset)中的6000张图像 和测试数据集(test dataset)中的1000张图像组成。 因此,训练集和测试集分别包含60000和10000张图像。 测试数据集不会用于训练,只用于评估模型性能。

每个输入图像的高度和宽度均为28像素。 数据集由灰度图像组成,其通道数为1。 为了简洁起见、将高度 ℎ 像素、宽度 𝑤像素图像的形状记为 ℎ×𝑤或( ℎ , 𝑤 )。

%matplotlib inline
import tensorflow as tf
from d2l import tensorflow as d2ld2l.use_svg_display()#执行之后会启动下载数据集到本地,不建议开飞机再运行此代码下载、亲测会丢包导致数据不完整而报错。
mnist_train, mnist_test = tf.keras.datasets.fashion_mnist.load_data()

如果下载失败(通常是这个文件t10k-images-idx3-ubyte.gz失败),也可以直接访问 Fashion-MNIST 数据集的官方网站或者仓库:Fashion-MNIST.

  • 下载数据集文件夹。

  • 将这四个文件放置在您项目的数据集目录中,例如 ~/.keras/datasets/fashion-mnist/。

下载失败的话删除~/.keras/datasets/fashion-mnist/文件夹重新运行直到成功为止。

Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
29515/29515 [==============================] - 0s 1us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26421880/26421880 [==============================] - 6s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
5148/5148 [==============================] - 0s 0s/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4422102/4422102 [==============================] - 1s 0us/step

Fashion-MNIST中包含的10个类别,分别为t-shirt(T恤)、trouser(裤子)、pullover(套衫)、dress(连衣裙)、coat(外套)、sandal(凉鞋)、shirt(衬衫)、sneaker(运动鞋)、bag(包)和ankle boot(短靴)。 以下函数用于在数字标签索引及其文本名称之间进行转换。

def get_fashion_mnist_labels(labels):  #@save"""返回Fashion-MNIST数据集的文本标签"""text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat','sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']return [text_labels[int(i)] for i in labels]
#创建一个函数来可视化这些样本
def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):  #@save"""绘制图像列表"""figsize = (num_cols * scale, num_rows * scale)_, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)axes = axes.flatten()for i, (ax, img) in enumerate(zip(axes, imgs)):ax.imshow(img.numpy())ax.axes.get_xaxis().set_visible(False)ax.axes.get_yaxis().set_visible(False)if titles:ax.set_title(titles[i])return axes

查看数据:

X = tf.constant(mnist_train[0][:10])
y = tf.constant(mnist_train[1][:10])
show_images(X, 2, 5, titles=get_fashion_mnist_labels(y));

在这里插入图片描述

2.读取小批量数据

为了在读取训练集和测试集时更容易,使用内置的数据迭代器,而不是从零开始创建。在每次迭代中,数据迭代器每次都会读取一小批量数据,大小为batch_size。 通过内置数据迭代器,可以随机打乱了所有样本,从而无偏见地读取小批量。

batch_size = 256
train_iter = tf.data.Dataset.from_tensor_slices(mnist_train).batch(batch_size).shuffle(len(mnist_train[0]))

数据迭代器是获得更高性能的关键组件。依靠实现良好的数据迭代器,利用高性能计算来避免减慢训练过程。

3.整合组件

def load_data_fashion_mnist(batch_size, resize=None):   #@save"""下载Fashion-MNIST数据集,然后将其加载到内存中"""mnist_train, mnist_test = tf.keras.datasets.fashion_mnist.load_data()# 将所有数字除以255,使所有像素值介于0和1之间,在最后添加一个批处理维度,# 并将标签转换为int32。process = lambda X, y: (tf.expand_dims(X, axis=3) / 255,tf.cast(y, dtype='int32'))resize_fn = lambda X, y: (tf.image.resize_with_pad(X, resize, resize) if resize else X, y)return (tf.data.Dataset.from_tensor_slices(process(*mnist_train)).batch(batch_size).shuffle(len(mnist_train[0])).map(resize_fn),tf.data.Dataset.from_tensor_slices(process(*mnist_test)).batch(batch_size).map(resize_fn))

通过指定resize参数来测试load_data_fashion_mnist函数的图像大小调整功能。

train_iter, test_iter = load_data_fashion_mnist(32, resize=64)
for X, y in train_iter:print(X.shape, X.dtype, y.shape, y.dtype)break

四、从0实现softmax回归

1.加载数据集

引入Fashion-MNIST数据集, 并设置数据迭代器的批量大小为256。

import tensorflow as tf
from IPython import display
from d2l import tensorflow as d2lbatch_size = 256
# load_data_fashion_mnist函数已封装在d2l模块里面可以直接调用。也可以调用上面的。
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

2.初始化模型参数

输出与类别一样多。 (因为数据集有10个类别,所以网络输出维度为10)。 因此,权重将构成一个 784×10 的矩阵, 偏置将构成一个 1×10 的行向量。 与线性回归一样,将使用正态分布初始化权重W,偏置初始化为0。

num_inputs = 784
num_outputs = 10W = tf.Variable(tf.random.normal(shape=(num_inputs, num_outputs),mean=0, stddev=0.01))
b = tf.Variable(tf.zeros(num_outputs))

3.定义softmax操作

实现softmax由三个步骤组成:

  • 对每个项求幂(使用exp);
  • 对每一行求和(小批量中每个样本是一行),得到每个样本的规范化常数;
  • 将每一行除以其规范化常数,确保结果的和为1。
def softmax(X):X_exp = tf.exp(X)partition = tf.reduce_sum(X_exp, 1, keepdims=True)return X_exp / partition  # 这里应用了广播机制

对于任何随机输入,将每个元素变成一个非负数。 此外,依据概率原理,每行总和为1。

4.定义模型

定义softmax操作后,可以实现softmax回归模型。 下面的代码定义了输入如何通过网络映射到输出。 注意,将数据传递到模型之前,使用reshape函数将每张原始图像展平为向量。

def net(X):return softmax(tf.matmul(tf.reshape(X, (-1, W.shape[0])), W) + b)

5.交叉熵损失函数

实现交叉熵损失函数

def cross_entropy(y_hat, y):return -tf.math.log(tf.boolean_mask(y_hat, tf.one_hot(y, depth=y_hat.shape[-1])))

6.分类精度

当预测与标签分类y一致时,即是正确的。 分类精度即正确预测数量与总预测数量之比。 虽然直接优化精度可能很困难(因为精度的计算不可导), 但精度通常是最关心的性能衡量标准,在训练分类器时几乎总会关注它。

def accuracy(y_hat, y):  #@save"""计算预测正确的数量"""if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:y_hat = tf.argmax(y_hat, axis=1)cmp = tf.cast(y_hat, y.dtype) == yreturn float(tf.reduce_sum(tf.cast(cmp, y.dtype)))

同样,对于任意数据迭代器data_iter可访问的数据集, 可以评估在任意模型net的精度。

def evaluate_accuracy(net, data_iter):  #@save"""计算在指定数据集上模型的精度"""metric = Accumulator(2)  # 正确预测数、预测总数for X, y in data_iter:metric.add(accuracy(net(X), y), d2l.size(y))return metric[0] / metric[1]

定义一个实用程序类Accumulator,用于对多个变量进行累加。 在上面的evaluate_accuracy函数中, 在Accumulator实例中创建了2个变量, 分别用于存储正确预测的数量和预测的总数量。 当遍历数据集时,两者都将随着时间的推移而累加。

class Accumulator:  #@save"""在n个变量上累加"""def __init__(self, n):self.data = [0.0] * ndef add(self, *args):self.data = [a + float(b) for a, b in zip(self.data, args)]def reset(self):self.data = [0.0] * len(self.data)def __getitem__(self, idx):return self.data[idx]

7.训练模型

定义一个函数来训练一个迭代周期。 请注意,updater是更新模型参数的常用函数,它接受批量大小作为参数。 它可以是d2l.sgd函数,也可以是框架的内置优化函数。

def train_epoch_ch3(net, train_iter, loss, updater):  #@save"""训练模型一个迭代周期(定义见第3章)"""# 训练损失总和、训练准确度总和、样本数metric = Accumulator(3)for X, y in train_iter:# 计算梯度并更新参数with tf.GradientTape() as tape:y_hat = net(X)# Keras内置的损失接受的是(标签,预测),这不同于用户在本书中的实现。# 本书的实现接受(预测,标签),例如我们上面实现的“交叉熵”if isinstance(loss, tf.keras.losses.Loss):l = loss(y, y_hat)else:l = loss(y_hat, y)if isinstance(updater, tf.keras.optimizers.Optimizer):params = net.trainable_variablesgrads = tape.gradient(l, params)updater.apply_gradients(zip(grads, params))else:updater(X.shape[0], tape.gradient(l, updater.params))# Keras的loss默认返回一个批量的平均损失l_sum = l * float(tf.size(y)) if isinstance(loss, tf.keras.losses.Loss) else tf.reduce_sum(l)metric.add(l_sum, accuracy(y_hat, y), tf.size(y))# 返回训练损失和训练精度return metric[0] / metric[2], metric[1] / metric[2]

训练函数实现之前,定义一个在动画中绘制数据的实用程序类Animator, 它能够简化本书其余部分的代码。

class Animator:  #@save"""在动画中绘制数据"""def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,ylim=None, xscale='linear', yscale='linear',fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,figsize=(3.5, 2.5)):# 增量地绘制多条线if legend is None:legend = []d2l.use_svg_display()self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize)if nrows * ncols == 1:self.axes = [self.axes, ]# 使用lambda函数捕获参数self.config_axes = lambda: d2l.set_axes(self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)self.X, self.Y, self.fmts = None, None, fmtsdef add(self, x, y):# 向图表中添加多个数据点if not hasattr(y, "__len__"):y = [y]n = len(y)if not hasattr(x, "__len__"):x = [x] * nif not self.X:self.X = [[] for _ in range(n)]if not self.Y:self.Y = [[] for _ in range(n)]for i, (a, b) in enumerate(zip(x, y)):if a is not None and b is not None:self.X[i].append(a)self.Y[i].append(b)self.axes[0].cla()for x, y, fmt in zip(self.X, self.Y, self.fmts):self.axes[0].plot(x, y, fmt)self.config_axes()display.display(self.fig)display.clear_output(wait=True)

实现一个训练函数, 它会在train_iter访问到的训练数据集上训练一个模型net。 该训练函数将会运行多个迭代周期(由num_epochs指定)。 在每个迭代周期结束时,利用test_iter访问到的测试数据集对模型进行评估。

def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):  #@save"""训练模型(定义见第3章)"""animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],legend=['train loss', 'train acc', 'test acc'])for epoch in range(num_epochs):train_metrics = train_epoch_ch3(net, train_iter, loss, updater)test_acc = evaluate_accuracy(net, test_iter)animator.add(epoch + 1, train_metrics + (test_acc,))train_loss, train_acc = train_metricsassert train_loss < 0.5, train_lossassert train_acc <= 1 and train_acc > 0.7, train_accassert test_acc <= 1 and test_acc > 0.7, test_acc

作为一个从零开始的实现,使用定义的 小批量随机梯度下降来优化模型的损失函数,设置学习率为0.1。

class Updater():  #@save"""用小批量随机梯度下降法更新参数"""def __init__(self, params, lr):self.params = paramsself.lr = lrdef __call__(self, batch_size, grads):d2l.sgd(self.params, grads, self.lr, batch_size)updater = Updater([W, b], lr=0.1)

之前定义的小批量随机梯度下降如下,已经封装在d2l模块中了:

def sgd(params, grads, lr, batch_size):  #@save"""小批量随机梯度下降"""for param, grad in zip(params, grads):param.assign_sub(lr*grad/batch_size)

训练模型50个迭代周期。 请注意,迭代周期(num_epochs)和学习率(lr)都是可调节的超参数。 通过更改它们的值,可以提高模型的分类精度。

num_epochs = 50
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)

在这里插入图片描述

8.预测

模型已经准备好对图像进行分类预测。 给定一系列图像,将比较它们的实际标签(文本输出的第一行)和模型预测(文本输出的第二行)。

def predict_ch3(net, test_iter, n=6):  #@save"""预测标签(定义见第3章)"""for X, y in test_iter:breaktrues = d2l.get_fashion_mnist_labels(y)preds = d2l.get_fashion_mnist_labels(tf.argmax(net(X), axis=1))titles = [true +'\n' + pred for true, pred in zip(trues, preds)]d2l.show_images(tf.reshape(X[0:n], (n, 28, 28)), 1, n, titles=titles[0:n])predict_ch3(net, test_iter)

在这里插入图片描述

五、简洁实现softmax回归

softmax回归的输出层是一个全连接层,只需在Sequential中添加一个带有10个输出的全连接层。

import tensorflow as tf
from d2l import tensorflow as d2l#加载数据集
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)# 初始化模型参数,仍然以均值0和标准差0.01随机初始化权重。
net = tf.keras.models.Sequential()
net.add(tf.keras.layers.Flatten(input_shape=(28, 28)))
weight_initializer = tf.keras.initializers.RandomNormal(mean=0.0, stddev=0.01)
net.add(tf.keras.layers.Dense(10, kernel_initializer=weight_initializer))# 交叉熵损失函数,在交叉熵损失函数中传递未规范化的预测,并同时计算softmax及其对数。
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)# 使用学习率为0.1的小批量随机梯度下降作为优化算法
trainer = tf.keras.optimizers.SGD(learning_rate=0.1)# 调用之前定义的训练函数训练模型
num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

这里为什么是d2l.train_ch3函数来训练而不是类似tf内置的model.fit()呢?因为train_ch3函数里面已经封装好了训练过程和可视化功能。利用Animator类来可视化训练进度。
在这里插入图片描述
预测:

d2l.predict_ch3(net, test_iter)

在这里插入图片描述

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

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

相关文章

red hat 6.9版本7.9版本 忘记root密码重置方法

文章目录 1、重置密码具体步骤Oracle Linux 6.9版本&#xff1a;1、开机到此界面按e2、在以下界面选择后按e3、选择相应内核后按e4、在此界面输入 single 回车5、回到内核界面后按b进入引导系统6、在#后输入passwd root修改密码7、修改密码&#xff1a;8、Reboot重启9、使用新密…

基于springboot+vue的企业人事管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

【初中生讲机器学习】12. 似然函数和极大似然估计:原理、应用与代码实现

创建时间&#xff1a;2024-02-23 最后编辑时间&#xff1a;2024-02-24 作者&#xff1a;Geeker_LStar 你好呀~这里是 Geeker_LStar 的人工智能学习专栏&#xff0c;很高兴遇见你~ 我是 Geeker_LStar&#xff0c;一名初三学生&#xff0c;热爱计算机和数学&#xff0c;我们一起加…

css常用的选择器介绍

CSS&#xff08;层叠样式表&#xff09;选择器是CSS规则的一部分&#xff0c;它用于选择和定位网页上的元素&#xff0c;以便将样式应用到这些元素上。CSS选择器的种类繁多&#xff0c;每种选择器都有其特定的用途、特点和效率。在这篇文章中&#xff0c;我们将讨论一些常用的C…

leetcode:134.加油站

解题思路&#xff1a;需要注意开始时的编号&#xff0c;有的可以走一圈&#xff0c;有的走不了 模拟过程&#xff1a;for循环主要是用来模拟线性的过程&#xff0c;而在这里它是环状的&#xff1b; 可以用暴力解法&#xff0c;但是在这里我用贪心来解决。 常见疑惑&#xff1…

行为树入门:BehaviorTree.CPP Groot2练习(叶子节点)(2)

以《行为树BehaviorTree学习记录1_基本概念》练习。 1 SequenceNode顺序控制节点 代码下载 git clone https://gitee.com/Luweizhiyuan2020/ros2_bt.git例程 1.1 sequence 顺序执行 下载版本SequenceNode1。 1.2 ReactiveSequence 异步执行 注意&#xff1a; ①only a…

LeetCode 刷题 [C++] 第141题.环形链表

题目描述 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&a…

安全防御-第六次

内容安全 攻击可能只是一个点&#xff0c;防御需要全方面进行 DFI和DPI技术--- 深度检测技术 DPI --- 深度包检测技术--- 主要针对完整的数据包&#xff08;数据包分片&#xff0c;分段需要重组&#xff09;&#xff0c;之后对数据包的内容进行识别。&#xff08;应用层&…

Python 实现Excel自动化办公(上)

在Python 中你要针对某个对象进行操作&#xff0c;是需要安装与其对应的第三方库的&#xff0c;这里对于Excel 也不例外&#xff0c;它也有对应的第三方库&#xff0c;即xlrd 库。 什么是xlrd库 Python 操作Excel 主要用到xlrd和xlwt这两个库&#xff0c;即xlrd是读Excel &am…

type may not be empty [type-empty]

原因是使用了规范commit信息的工具&#xff0c;你的提交信息不符合规范&#xff0c;所以被拒绝了 commit规范工具 commitlinthusky 解决方式一&#xff1a; 修改提交信息&#xff0c; 使其符合规范 git commit -m "feat: 新功能"使用Git Gui的使用以下格式写提交…

安装使用zookeeper

先去官网下载zookeeper&#xff1a;Apache ZooKeeper 直接进入bin目录&#xff0c;使用powerShell打开。 输入: ./zkServer.cmd 命令&#xff0c;启动zookeeper。 zookeeper一般需要配合Dubbo一起使用&#xff0c;作为注册中心使用&#xff0c;可以参考另一篇博客&#xf…

探索IP地址定位工具:解读IP数据云的功能与优势

在当今数字化时代&#xff0c;IP地址定位工具成为了许多领域中不可或缺的技术支持&#xff0c;为网络安全、地理定位服务和个性化推荐等提供了重要数据支持。其中&#xff0c;IP数据云作为一种领先的IP地址定位工具&#xff0c;具有一系列功能和优势&#xff0c;本文将对其进行…