分类 classificaton

news2024/11/13 3:32:30

b10049398220446a9618e92493250956.png

1)什么是分类?

在此之前,我们一直使用的都是回归任务进行学习;这里我们将进一步学习什么是分类,我们先从训练模型的角度来看看二者的区别。

82a92f15506b468a9e2e416f68dd3d04.png

 对于回归来说,它所作的是对模型输入相应的特征,然后模型给出相应的输出,需要让模型的输出和实际的标签值越接近越好;而对于分类来说,同样的是将相应的特征输入模型,模型输出相应的类型。

1.1问题一:

分类模型的输出不像回归模型一样输出是一个特定的数值,所以对于分类模型来说我们可以根据将不同的类别使用不同的数值来代替。

例如:

class1 --- 1

class2 --- 2

class3 --- 3

这样当模型认为输入的特征和class1更符合的话就会输出1,class3更符合的话就会输出3。但是又会出现新的问题,采用以上编码方式是否会导致模型认为class1和class2更相似;class1和class3更加的不同呢?因为class1和class2的距离上更近,比如网络的输出值是1.49,其实就表示很大概率是class1或者class2,这样其实就隐含表示class1或者class2更近一点。

1.2问题二:

对于有些分类任务来说,采用以上编码方式是不会具有问题的。比如使用升高和体重来预测小朋友的年级,例如:一年级 --- 1、二年级 --- 2、三年级 --- 3。这样是没问题的,因为一年级和二年级这两个类别来说是相对更近的, 一年级和三年级这两个类别来说是相对更远的。但是对于有的分类任务来说,再编码的时候就会产生这样的问题,于是在编码的时候采用one-hot vector的方式进行编码。

例如:

 eq?class1%3D%5Cleft%20%5B%20%5Cbegin%7Bmatrix%7D%201%20%5C%5C%200%20%5C%5C%200%20%5C%5C%20%5Cend%7Bmatrix%7D%20%5Cright%20%5D

 eq?class2%3D%5Cleft%20%5B%20%5Cbegin%7Bmatrix%7D%200%20%5C%5C%201%20%5C%5C%200%20%5C%5C%20%5Cend%7Bmatrix%7D%20%5Cright%20%5D

 eq?class3%3D%5Cleft%20%5B%20%5Cbegin%7Bmatrix%7D%200%20%5C%5C%200%20%5C%5C%201%20%5C%5C%20%5Cend%7Bmatrix%7D%20%5Cright%20%5D

采用这种编码方式的话,就不会出现以上这种问题了,这样的话,他们之间的距离都是一样的啦。

1.3问题三:

在回归问题的时候,我们构建的神经网络只能输出一个数值,但是对于分类问题来说,要是采用one-hot的编码方式对类别进行编码,那么对于网络的输出就不能只有一个,所以网络的结构也必须改变。

所以如下图所示,只需要多输出两个就行。

58aaed9f9f3b4fd5bf2b507ae315ddf7.png

至此对于一个分类任务的模型我们已经构架完成了,只不过是对于回归问题进行了一些小小的改进,但是其实对于分类问题来说,还有一些不太一样的问题。


 我们来最终对比一下回归任务和分类任务,分类任务最终的输出要和实际的标签纸越接近越好;对于分类来说,其最终的输出也应该与实际的类别标签纸越接近越好。

fbfc23417b5245f09ddfac263253aa7d.png

可是实际在最后一步输出的过程中,即最后网络输出了y之后,对于分类问题来说会再加上一个softmax使其输出 eq?y%5E%7B%27%7D ,然后希望的是  eq?y%5E%7B%27%7D和实际的标签值越接近越好。

2)为什要加softmax?

简单理解即使,其实对于网络的输出的三个值是可以为任何值,但是在最终的标签我们是希望在零到一之间的,所以通过softmax就可以将网络输出的值规格化到零到一之间。

softmax的工作过程:

  • 对其所以的输入的y值(网络最后的输出)取exp,也就是分别计算e^{y_i}
  • e^{y_i}进行求和
  • 用分别计算得到的e^{y_i}比上所有e^{y_i}的和就得到了每个数值最后被规格化后的数值。

例子:

输入softmax的三个数值是3、1、-3。 

  • 取exp。得到e^3=20,e^1=2.7,e^{-3}=0.05
  • 求和。20 + 2.7 + 0.05 = 22.75
  • 归一化。\frac{0.05}{22.75}\approx 0,\frac{2.7}{22.75}\approx 0.12,\frac{20}{22.75}\approx 0.88

其实在实际的分类问题当中,当分类任务是两个类别的时候,我们更常用的是使用sigmoid函数来进行最后的归一化;但是其实也可以使用softmax,他们二者在二分类的使用上无本质的区别。

2.1sigmoid函数:

sigmoid函数原型表达式如下:

                                                              sigmoid = \frac{1}{1+e{-x}}

以输入x_1,x_2为例子。

  • sigmoid:output(x_1)=\frac{1}{1+e{-x_1}}
  • softmax:output(x_1)=\frac{e^{x_1}}{e^{x_1}+e^{x_2}}=\frac{1}{1+e^{x_2-x_1}}=\frac{1}{1+e^{-(x_1-x_2)}}
  • 对于二分类来说可以进一步将softmax写成:softmax =\frac{1}{1+e^{-z_1}}

由此可得对于二分类问题来说,其二者的公式无本质区别,即理论上来说,二者是没有任何区别的。

sigmoid和softmax函数的本质区别: 

sigmoid函数用于多标签分类问题,选取多个标签作为正确答案,它是将任意值归一化为[0-1]之间,并不是不同概率之间的相互关联

Softmax函数用于多分类问题,即从多个分类中选取一个正确答案。Softmax综合了所有输出值的归一化,因此得到的是不同概率之间的相互关联。 

转载来源:深度学习随笔——Softmax函数与Sigmoid函数的区别与联系 - 知乎

Sigmoid函数针对两点分布提出。神经网络的输出经过它的转换,可以将数值压缩到(0,1)之间,得到的结果可以理解成分类成目标类别的概率P,而不分类到该类别的概率是(1 - P),这也是典型的两点分布的形式。

Softmax函数本身针对多项分布提出,当类别数是2时,它退化为二项分布。而它和Sigmoid函数真正的区别就在——二项分布包含两个分类类别(姑且分别称为A和B),而两点分布其实是针对一个类别的概率分布,其对应的那个类别的分布直接由1-P得出。

简单点理解就是,Sigmoid函数,我们可以当作成它是对一个类别的“建模”,将该类别建模完成,另一个相对的类别就直接通过1减去得到。而softmax函数,是对两个类别建模,同样的,得到两个类别的概率之和是1。

3)分类问题的损失函数

分类问题的损失函数同样的根据距离来计算,可以和之前的回归问题一样使用MSE误差来计算损失函数。但是更常用是使用下图中的 Cross- entropy来计算误差

3.1 为什么选择Cross- entropy而不是Mean Square error

其实我们实际在使用pytorch进行构建网络实现分类的任务的时候,我们会发现找不到softmax,这是因为,我们再构建网络后,使用Cross-entropy来计算误差的时候,其会自动再网络的最后一层加上softmax,在 pytorch中Cross-entropy和softmax被内置成为了一个整体。

例子:

在某个训练过程中网络输出的三个数值分别是y_1,y_2,y_3,然后再经softmax处理得到了最后的输出结果,我们分别使用Cross- entropy和Mean Square error进行求损失,然后根据损失计算下一步该往哪里走。 

如上图所示得到了Cross- entropy和Mean Square error 的损失图,在图的右下角都是损失最小的地方,即y_1的值变大, y_2的值变小就可以使得损失值变小;在图的左上角都是损失最大的地方,即y_1的值变小, y_2的值变大会使得损失值变大。 

所以当使用Mean Square error 的时候,很有可能会到达梯度不变的点,导致训练不下去(一般的训练过程训练不下去),但是对于 Cross- entropy 来说,确是有梯度了,可以进行下去。 

所以可以得出对于损失函数的设计都会影响最后的一个训练优化过程。

4)pytorch实现分类代码

完整代码:

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms

# 定义超参数
batch_size = 64
learning_rate = 0.001
num_epochs = 10

# 数据预处理
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

# 下载并加载 CIFAR-10 数据集
train_dataset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)

test_dataset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

# 定义 CNN 模型
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)
        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(64 * 8 * 8, 512)
        self.fc2 = nn.Linear(512, 10)
        self.relu = nn.ReLU()

    def forward(self, x):
        x = self.pool(self.relu(self.conv1(x)))
        x = self.pool(self.relu(self.conv2(x)))
        x = x.view(-1, 64 * 8 * 8)
        x = self.relu(self.fc1(x))
        x = self.fc2(x)
        return x

# 实例化模型、定义损失函数和优化器
model = SimpleCNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)

# 训练模型
for epoch in range(num_epochs):
    running_loss = 0.0
    for i, (inputs, labels) in enumerate(train_loader):
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        if i % 100 == 99:    # 每100个mini-batch打印一次
            print(f'Epoch [{epoch + 1}/{num_epochs}], Step [{i + 1}], Loss: {running_loss / 100:.4f}')
            running_loss = 0.0

print('训练完成')

# 测试模型
model.eval()
correct = 0
total = 0
with torch.no_grad():
    for images, labels in test_loader:
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print(f'测试准确率: {100 * correct / total:.2f}%')

接下来将一步一步剖析代码,我们先看网络架构。

4.1 __init__ 方法中初始化和声明网络的各层

定义SimpleCNN类,让其继承prtorch中的nn.Module,并且

# 定义 CNN 模型
class SimpleCNN(nn.Module): #SimpleCNN 继承自 nn.Module
    def __init__(self): #构造函数(初始化方法)
        super(SimpleCNN, self).__init__()  # 这一行代码调用了父类(nn.Module)的构造函数,确保在实例化SimpleCNN类时,也会执行父类的初始化操作。       

网络1——卷积1:

self.conv1 = nn.Conv2d(3, 32, 3, padding=1)

CIFAR-10图像都是3通道(RGB)的,尺寸为32x32像素。因此每个图像的数据形状是3x32x32,所以对于32个卷积核

  • 输入通道数:3,对应于 RGB 图像的三个通道。
  • 输出通道数:32,卷积核的数量,生成32个特征图。
  • 卷积核大小:3x3。
  • Padding:1,保持输出和输入的宽高相同。

CIFAR-10图像为3x32x32,输入通道是3,其实就对应着3个特征图,即RGB三个特征图。经过32个卷积核采样后,每个卷积核都能得到一个特征图,也就是32个特征图。假设一张3x6x6的图片在经32个卷积核采样在不加padding的时候得到的特征图都是4x4的,加了padding之后,就能保证最后卷积后得到额特征是还是6x6的。而CIFAR-10图像为3x32x32,所以其在经过32个卷积核且加了padding采样后最终得到是32(32维度特征)x32x32(32x32为图片的大小padding保证)

网络1——卷积2:

self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
  • 输入通道数:32,来自前一层的输出。
  • 输出通道数:64。
  • 卷积核大小:3x3。
  • Padding:1。

最终得到是64(64维度特征)x32x32(32x32为图片的大小padding保证)

 网络2——池化层

self.pool = nn.MaxPool2d(2, 2)
  • 池化大小:2x2。
  • 步幅:2,减少特征图尺寸,通常用于降采样。

其实就是缩小特征图的大小的,使用2x2的池化后,特征图的大小缩小;由于是2x2池化且步幅为2,若图片尺寸初始为8x8,那么最后的尺寸将变为4x4。

网络3——全连接层1

self.fc1 = nn.Linear(64 * 8 * 8, 512)
  • 输入大小:64 * 8 * 8,来自池化层展平后的特征数。
  • 输出大小:512,隐藏层的神经元个数。

网络3——全连接层2

self.fc2 = nn.Linear(512, 10)
  • 输入大小:512,来自前一层的输出。
  • 输出大小:10,对应于 CIFAR-10 数据集的10个类别。

激活函数

self.relu = nn.ReLU()

ReLU:一种常用的激活函数,引入非线性,计算简单,能有效缓解梯度消失问题。

__init__ 方法中初始化和声明这些网络的各层,并设置了每层的参数和属性,例如输入通道数、输出通道数、卷积核大小,接下去将开始构建这个网络。

4.2 前向传播

def forward(self, x):
    x = self.pool(self.relu(self.conv1(x)))

定义前向传播函数,需要注意的是前向传播函数的名称必须是 forward。这是因为 nn.Module__call__ 方法在内部会默认调用 forward 方法来执行前向传播。如果使用其他名称,__call__ 方法将无法找到并执行前向传播。

  •  在 forward 函数参数列表中的x为输入的图片数据。
  • 池化( 激活函数( 卷积网络(输入参数input)))= 输出参数
  x = self.pool(self.relu(self.conv2(x)))

同样是一层卷积网络,其输入的CIFAR-10图像为3x32x32,经过卷积池化卷积池化,其最后的输出为(batch的大小,64x8x8),两次池化将32x32大小的图片变为8x8的大小。
   

x = x.view(-1, 64 * 8 * 8)
  • view方法用于改变张量的形状,使其成为一维张量,前面的参数-1,表示根据batch的数量来定最后展开的张量大小。
    x = self.relu(self.fc1(x))
    x = self.fc2(x)
    return x
  • 将展开的一维张量输入到全连接层,最后得到网络的输出

到现在为止其实我们没有看见在最后一个全连接层没有加任何的softmax和sigmoid函数,但是对于分类任务来说,最后的输出都应该体现为概率,即需要将最后的结果映射为0到1之间。其实也就是我们提到的,要是我们使用的是Cross-entropy来计算误差的话,对于pytorch来说,其会自动在最后一层加上softmax。

4.3 例化模型、定义损失函数和优化器

model = SimpleCNN()
  • 实例化了一个 SimpleCNN 类的对象,即创建了模型。SimpleCNN 继承自 nn.Module,并且在其中定义了网络的各个层前向传播的方法。当调用 SimpleCNN() 时,执行 __init__() 构造函数来初始化所有层(卷积层、池化层、全连接层等),从而定义了模型的结构。
criterion = nn.CrossEntropyLoss()   # Cross-entropy来计算误差而不是Mean Square error
  • nn.CrossEntropyLoss() 是 PyTorch 中用于 多分类问题 的常用损失函数,会自动在最后加上softmax。
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
  • Adam(Adaptive Moment Estimation)是一种基于梯度下降的优化算法,它结合了我们之前学习的动量(Momentum),以及自适应学习率(AdaGrad)的方法。
    • model.parameters():获取模型中所有需要优化的参数,通常是卷积层和全连接层的权重和偏置。
    • lr=learning_rate:指定学习率 learning_rate,它决定了每次更新时步长的大小。学习率较大时,参数更新可能较快,但容易跳过最优解;学习率较小时,参数更新缓慢,可能会收敛得更好,但需要更多的迭代次数。

Adam 具有较好的性能,能够在许多任务中较快地收敛,且对超参数的选择不那么敏感。它适用于大部分神经网络训练场景,尤其是在深度学习中常常被使用。同时是一种自适应优化方法,它可以动态调整每个参数的学习率,因此可以有效地避免过大的梯度波动。

4.4 测试和训练

①训练

第一层循环,循环epoch次,同时初始化running_loss为零。

for epoch in range(num_epochs):
    # 用于累加当前 epoch 中每个 mini-batch 的损失值,最后用来计算平均损失。
    running_loss = 0.0

第二层循环,前面设置的batch_size = 64;其中 train_loader 负责从训练数据集中加载批次数据(mini-batch),通常会自动分配数据到多个 GPU 或 CPU 上;所以该层循环遍历训练数据集和,但是每一个遍历实际看过的数据是mini-batch,而不是我们自己设置的batch大小。

for i, (inputs, labels) in enumerate(train_loader):
  • inputs是实际输入的训练数据图片
  • labels是实际输入的训啦数据图片的标签。
  • i 是统计mini-batch的数量。

第二层循环体内部:先将优化器中的梯度清零;将图像数据送入model,进入网络的前向推理阶段,得到网络的输出值outputs,然后使用Cross-entropy来计算误差。

 optimizer.zero_grad()
 outputs = model(inputs)  # inputs 输入模型就会自动调用前向传播代码
 loss = criterion(outputs, labels)  # Cross-entropy来计算误差(输出值,标签值)

这里也能反映我们当时在学习batch的时候说,不同的batch,我们使用不同的损失函数来进行更新梯度会使得我们在训练的时候更好的跳开local minima。

对损失函数求导,链式法则逐层计算梯度。

loss.backward()

优化算法,优化更新模型的参数(权重和偏置)

optimizer.step()

②测试

model.eval()

eval() 方法将模型设置为评估模式(evaluation mode)。在此模式下,PyTorch 会关闭一些只在训练时需要的操作,如 DropoutBatch Normalization。这些操作在测试过程中是不需要的,因为我们希望评估模型的最终表现,而不是其在训练过程中随机的行为。设置为 eval() 后,模型会以确定的方式进行预测。

correct = 0
total = 0
with torch.no_grad():

torch.no_grad() 中进行的操作不会计算梯度,因此更加高效

 _, predicted = torch.max(outputs.data, 1)

torch.max(outputs.data, 1) outputs.data 是模型的输出,但是模型可以同时预测多个样本,所以对于模型的输出来说,其输出的张量就会含有两个维度,第一个维度(dim=0,即行数)是样本的数量;第二个维度(dim=1,即列数)即类别维度。 

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

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

相关文章

Maven学习——创建Maven的Java和Web工程,并运行在Tomcat上

一、Maven介绍 Maven 是一款为 Java 项目管理构建、依赖管理的工具(软件),使用 Maven 可以自动化构建、测试、打包和发布项目,大大提高了开发效率和质量。 二、Maven安装步骤 1.下载后解压到没有空格、特殊字符和中文的目录中 2…

数据血缘追踪是如何在ETL过程中发挥作用?

在大数据环境下,数据血缘追踪具有重要意义,它能够帮助用户了解数据的派生关系、变换过程和使用情况,进而提高数据的可信度和可操作性。通过数据血缘追踪,ETL用户可以准确追溯数据的来源,快速排查数据异常和问题。 一、…

在服务器里安装2个conda

1、安装新的conda 下载地址:Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 本文选择:Anaconda3-2023.03-1-Linux-x86_64.sh 安装:Ubuntu安装Anaconda详细步骤(Ubuntu22.04.1&#xff…

OceanBase中,如何解读 obdiag 收集的火焰图 【DBA早下班系列】

1. 前言 在之前的文章 遇到性能问题,如何给OceanBase“拍CT“(火焰图与扁鹊图)中,分享了obdiag 快速收集火焰图的方法,那么,紧接着的问题便是:收集到火焰图和扁鹊图之后,该如何解读…

【数据库实验一】数据库及数据库中表的建立实验

目录 实验1 学习RDBMS的使用和创建数据库 一、 实验目的 二、实验内容 三、实验环境 四、实验前准备 五、实验步骤 六、实验结果 七、评价分析及心得体会 实验2 定义表和数据库完整性 一、 实验目的 二、实验内容 三、实验环境 四、实验前准备 五、实验步骤 六…

基于YOLO11/v10/v8/v5深度学习的煤矿传送带异物检测系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

【鸿蒙】HarmonyOS NEXT应用开发快速入门教程之布局篇(下)

系列文章目录 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(上) 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(下) 【鸿蒙】HarmonyOS NEXT应用开发快速入门教程之布局篇(上) 【…

设计模式-七个基本原则之一-单一职责原则 + SpringBoot案例

单一职责原理:(SRP) 面向对象七个基本原则之一 清晰的职责:每个类应该有一个明确的职责,避免将多个责任混合在一起。降低耦合:通过将不同的职责分开,可以降低类之间的耦合度,提高系统的灵活性。易于维护:当…

【Linux】进程信号全攻略(一)

🌈 个人主页:Zfox_ 🔥 系列专栏:Linux 目录 一:🔥 信号的概念 二:🔥 信号产生的方式 🦋 使用键盘🦋 系统调用函数🦋 软件条件🦋 进程异…

selenium+chromedriver下载与安装

安装selenium 使用pip安装selenium: pip install selenium安装成功: 安装WebDriver 根据你使用的浏览器下载相应的 WebDriver。 Chrome:下载地址Firefox:下载地址Edge:下载地址Safari:下载地址 1、c…

前端环境配置

对于换公司的小伙伴来讲,重新安装环境,百度或许稍微有点麻烦,本文章让你无脑式直接操作,保证环境畅通无阻。 1.安装nvm-setup 该插件是一款管理nodeJs的包,无需你单独下载nodeJs去安装,只需要下载安装此…

python(自用查看版)

目录 1.注意事项 1.1 python的除法不是整除,得到的是浮点数 1.2算术符号基于数学的算术优先级。具体可自行查看。 1.3注释 1.4缩进 1.5换行 1.6常见关键字 1.7续行符 1.8报错 1.9赋值 1.10比较运算符 2.常量和表达式 3.变量 4.数据类型 4.1整型int …

基于Prometheus的client_golang库实现应用的自定义可观测监控

文章目录 1. 安装client_golang库2. 编写可观测监控代码3. 运行效果4. jar、graalvm、golang编译运行版本对比 前文使用javagraalvm实现原生应用可观测监控: prometheus client_java实现进程的CPU、内存、IO、流量的可观测,但是部分java依赖包使用了复杂…

Docker网络概述

1. Docker 网络概述 1.1 网络组件 Docker网络的核心组件包括网络驱动程序、网络、容器以及IP地址管理(IPAM)。这些组件共同工作,为容器提供网络连接和通信能力。 网络驱动程序:Docker支持多种网络驱动程序,每种驱动程…

新160个crackme - 094-TheBigMan-crackme6

运行分析 需破解Name和Serial PE分析 LCC win32程序,32位,无壳 静态分析&动态调试 ida搜索字符串,进入关键函数 ida动调,发现关键判断函数func_1 进入后,发现Name长度需满足一定要求,且func_2返回值不能…

大数据-214 数据挖掘 机器学习理论 - KMeans Python 实现 算法验证 sklearn n_clusters labels

点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…

【Django】视图函数

【Django】视图函数 视图函数的本质是Python中的函数,视图函数负责处理用户的请求并返回响应,该响应可以是网页的HTML内容、重定向、404错误、XML文档、图像或者任何东西,一般在应用中的views.py编写,示例代码如下: …

kafka+zookeeper的搭建

kafka从2.8版本开始,就可以不用配置zookeeper了,但是也可以继续配置。我目前使用的kafka版本是kafka_2.12-3.0.0.tgz,其中前面的2.12表示是使用该版本的scala语言进行编写的,而后面的3.00才是kafka当前的版本。 通过百度网盘分享…

了解bootstrap改造asp.net core MVC的样式模板

我们都知道,在使用默认的asp.net core MVC模板建立项目的时候,里面的样式是已经事先被写好了的。一般来说都在css目录下的site.css和bootstrap.css及下面的bootstrap.min.css中。我们打开bootstrap这些样式文件,里面有大量的样式类的定义&…

HTMLCSS:旋转的动态卡片

效果演示 这段代码创建了一个具有动态背景和渐变效果的卡片。卡片背景有一个无限循环的旋转动画&#xff0c;增加了视觉吸引力。这种效果可以用于展示个人信息、项目介绍或其他需要吸引用户注意的内容。 HTML <div class"card"><h3>前端Hardy</h3&…