李宏毅-21-hw3:对11种食物进行分类-CNN

news2024/11/25 14:29:16

一、代码慢慢阅读理解+总结内化:

1.关于torch.nn.covd2d()的参数含义、具体用法、功能:

(1)参数含义:

注意,里面的“padding”参数:《both》side所以是上下左右《四》边都会加一个padding数量的0列:

证明如下:

import torch

x = torch.randn(3,1,5,4)
print(x)

conv = torch.nn.Conv2d(1,4,3,1,1)
res = conv(x)

print(res.shape)    # torch.Size([3, 4, 5, 4])
#所以说,很明显,只要padding的参数设置为1 + filter大小为3*3,那么输出的图像高、宽==输入的高、宽

运行结果:torch.Size([3, 4, 5, 4]

(2)具体用法:

import torch

x = torch.randn(3,1,5,4)
print(x)

conv = torch.nn.Conv2d(1,4,(2,3))
res = conv(x)

print(res.shape)    # torch.Size([3, 4, 4, 2])

输入:x[ batch_size, channels, height_1, width_1 ]
batch_size,一个batch中样本的个数 3
channels,通道数,也就是当前层的深度 1
height_1, 图片的高 5
width_1, 图片的宽 4

卷积操作:Conv2d[ channels, output, height_2, width_2 ]
channels,通道数,和上面保持一致,也就是当前层的深度 1
output ,输出的深度 4【需要4个filter】
height_2,卷积核的高 2
width_2,卷积核的宽 3

输出:res[ batch_size,output, height_3, width_3 ]
batch_size,,一个batch中样例的个数,同上 3
output, 输出的深度 4
height_3, 卷积结果的高度 4
width_3,卷积结果的宽度 2

(3)功能:

里面实现的功能,应该就是实现利用自己设定数量的filters,进行按照自己设定的stride、padding的方式对整个图像进行二维卷积,得到一个新的channel的图像,

新的channels的数目 == filters的数目,

至于输出的图像的size,需要自己进行计算一下!!!

2.torch.nn.BatchNorm2d()的参数含义、用法、功能作用:

(1)参数含义:

(2)用法示例:

里面看到的randn()

输入:x[ batch_size, channels, height_1, width_1 ]
batch_size,一个batch中样本的个数 2,也就是有2个tensor张量,后面3是张量的厚、高、宽

channel的大小是3

高、宽都是2

import torch.nn as nn
import torch
if __name__ == '__main__':
    bn = nn.BatchNorm2d(3)
    ip = torch.randn(2, 3, 2, 2)
    print(ip)
    output = bn(ip)
    print(output)

(3)功能作用:

BatchNorm为什么NB呢,关键还是效果好。不仅仅极大提升了训练速度,收敛过程大大加快;②还能增加分类效果,一种解释是这是类似于Dropout的一种防止过拟合的正则化表达方式,所以不用Dropout也能达到相当的效果;③另外调参过程也简单多了,对于初始化要求没那么高,而且可以使用大的学习率等。总而言之,经过这么简单的变换,带来的好处多得很,这也是为何现在BN这么快流行起来的原因。

具体参见这一篇文章:白话详细解读(七)----- Batch Normalization_底层研究生的博客-CSDN博客

3.torch.nn.MaxPool2d()最大池化(pooling)函数:

(1)参数含义:

nn.MaxPool2d(2, 2, 0), #但是pooling会改变图像的大小,图像会变成64*(128/2)*(128/2)

第一个“2”: 代表kernel_size,也就是窗口的大小,这里只有1个数值,那就是正方形的了

第二个“2”:代表stride,这里只有1个数值,那么就是向右的时候2个,向下的时候,也是2个

第三个“0”:代表在4个边加padding层的层数

(2)用法示例:

torch.nn.MaxPool2d详解_Medlen的博客-CSDN博客

具体可以参见这一篇博客,每个参数的用法讲述得非常详细

(3)作用:

主要是为了减少图像的高、宽size,和图像压缩的思想一致,也是利用了对于“下采样”的话,人眼对图像的感知是不会发生改变的

4.DataLoader的使用-初探:

(1)一个最基础的实例:

import torch
from torch.utils.data import Dataset, DataLoader
#下面逐步分析如下创建Dataset 和 DataLoader的示例代码

#1.从已经定义好的Dataset基类中继承得到Plus1Dataset类
class Plus1Dataset(Dataset):
     def __init__(self, a=0, b=1): #(1)定义这个类的构造函数,self是固定的要求,a,b是自己设置的变量
         super(Dataset, self).__init__()#继承得到积累的构造函数
         assert a <= b #需要a<=b,否则终断开(断言语法)
         self.a = a
         self.b = b
        
     def __len__(self):
         return self.b - self.a + 1 #(2)定义len函数,返回b-a+1
        
     def __getitem__(self, index): #(3)定义getitem函数,有一个参数index,一般都是返回这个index位置的那一行数据
        assert self.a-1 <= index <= self.b-1
        return index, index+1
#2.实例化创建Plus1Dataset和DataLoader的对象
data_train = Plus1Dataset(a=1,b=16)
data_train_loader = DataLoader(data_train, batch_size=4, shuffle=True)
print(len(data_train))  

从这个实例可以看出,

Dataset只是一个数据的容器,它是Loader的一部分

但是呢,DataLoader里面不仅有数据,还有对数据进行处理的方法,比如batch_size的大小,是否shuffle等

(2)http://t.csdn.cn/6H0LG

这个文章里面讲述得还算比较清晰,不过需要下载CIFAR-10的数据集

5.利用matplotlib中的plt.imread读取(同一个文件夹下),plt.imshow和plt.show打印输出图像
import matplotlib.pyplot as plt

test = plt.imread("./00000000.png")

plt.imshow(test)
plt.axis('off')  # 关闭坐标轴
plt.show()

6.使用PIL库中的Image进行操作图像:
from PIL import Image

# Load the image
image = Image.open("image.jpg")

# Save the image with a new name and format
output_path = "output.png"
image.save(output_path, "PNG")

# Show the output path
print("Image saved at:", output_path)

通过Image.open打开的对象可以直接作为transforms的参数,它可以和numpy.array进行转换,上面用plt打开的方式其实得到的是numpy.array对象,不能直接transform

7.torchvision.transforms模块-初探:

慢慢阅读学习+自己实践一下是否可以对一个图像进行这样的处理

import matplotlib.pyplot as plt
from PIL import Image
test = plt.imread("./00000000.png")
test2 = Image.open("00000000.png")

#plt.imshow(test)
#plt.axis('off')

#plt.show()
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]



import torchvision.transforms as transforms
train_transforms =transforms.Compose([
    transforms.Resize((500,500)), #这个函数可以将图像转变为统一的500*500的大小
    #transforms.CenterCrop(300), #这个就是从中心裁剪300*300的大小的图片,原来其他部分都不要了
    #transforms.RandomCrop(300), #随机裁剪出一个300*300部分
    #transforms.RandomHorizontalFlip(0.5),#以0.5个概率进行水平翻转
    #transforms.RandomRotation(degrees=45),#在不超过45度的范围内进行随机旋转
    #transforms.Normalize(mean = mean,std=std) #反正就是 归一化,没什么好说的
    #transforms.ToTensor(),#这个用得挺多的,就是将图像转换为tensor的数据类型
    #transforms.ColorJitter(brightness=0.5,contrast=0.5,saturation=0.5,hue=0.5),#分别设置亮度,对比度,饱和度,色调的偏差范围[随机]
    #transforms.Grayscale(), #将图像转变为channel==1的灰度图
    
    
])

img1 = train_transforms(test2)

#下面是存储一个Image类型的图片放到该目录下的方式
save_path = "test_output.png"
img1.save(save_path,"PNG")
print("Image as below:",save_path)

img1.show() #似乎Image只有这个函数可以显示图像,而且是用默认图像查看器打开的,算了,就这样吧
#和上面的plt有些不同plt是在下面输出显示绘制



8.torch.nn.Softmax()函数讲解:

Pytorch nn.Softmax(dim=?) - 知乎 (zhihu.com)

这篇文章中详细讲解了 Softmax函数中的dim参数的用法:

这篇文章描述得非常清晰,比chatGPT讲的好多了

9.防止梯度爆炸的函数utils.clip_grad_norm(,)的用法:

10.调用argmax函数,在最后一个维度中的每一组的抽取出max值的位置索引组成一个向量,用于和label中的数据进行比较,从而计算accuracy:

二、定义的基本classifier模型:

class Classifier(nn.Module): #这里定义了这个CNN食物图像分类的nuaral network结构
    def __init__(self):
        super(Classifier, self).__init__()
        # The arguments for commonly used modules:
        # torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding)
        # torch.nn.MaxPool2d(kernel_size, stride, padding)

        # input image size: [3, 128, 128]
        #需要我进一步进行慢慢学习的是:
        #(1)这个Convd函数的参数的意义?以及具体的实现是什么?
        #答:里面实现的功能,应该就是实现利用自己设定数量的filters,进行按照自己设定的stride、padding的方式对整个图像进行二维卷积,得到一个新的channel的图像
        #...具体见csdn
        #(2)这个BatchNorm2d函数的参数的意义?以及实现的功能是什么
        #(3)这里特别需要注意的是,这次用的数据是图像,最开始有对图像进行transform.resize(128,128),所以图像的pixel大小应该是3*128*128
        #所以,这个图像数据到底经历了什么?
        self.cnn_layers = nn.Sequential(
            nn.Conv2d(3, 64, 3, 1, 1), #通过我的计算,输出的大小应该是64[厚]*128[高]*128[宽]
            nn.BatchNorm2d(64), #参数是channels(filters)的数量,只会改变数据分布,不会改变数据形状
            nn.ReLU(), #先通过BN,再使用ReLU是真的香,这样就可以最大化的利用BN得到的(0,1)正太分布了
            nn.MaxPool2d(2, 2, 0),#但是pooling会改变图像的大小,图像会变成64*(128/2)*(128/2)

            nn.Conv2d(64, 128, 3, 1, 1),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(2, 2, 0),#图像大小128*32*32

            nn.Conv2d(128, 256, 3, 1, 1),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(4, 4, 0),#图像大小256*8*8
        )
        self.fc_layers = nn.Sequential(
            nn.Linear(256 * 8 * 8, 256),
            nn.ReLU(),
            nn.Linear(256, 256),
            nn.ReLU(),
            nn.Linear(256, 11)
        )

    def forward(self, x):
        # input (x): [batch_size, 3, 128, 128]
        # output: [batch_size, 11]

        # Extract features by convolutional layers.
        x = self.cnn_layers(x)

        # The extracted feature map must be flatten before going to fully-connected layers.
        x = x.flatten(1) #需要展平之后,才能调用Linear()层

        # The features are transformed by fully-connected layers to obtain the final logits.
        x = self.fc_layers(x)
        return x

三、定义get_pseudo_labels函数:

这个函数,就是为了使用哪些没有label的数据,从而实现semi-unsupervised的训练方式,这里暂时先不考虑

def get_pseudo_labels(dataset, model, threshold=0.65): #参数是dataset,model和门槛
    # This functions generates pseudo-labels of a dataset using given model.
    # It returns an instance of DatasetFolder containing images whose prediction confidences exceed a given threshold.
    # You are NOT allowed to use any models trained on external data for pseudo-labeling.
    device = "cuda" if torch.cuda.is_available() else "cpu"#设备选择

    # Construct a data loader.
    data_loader = DataLoader(dataset, batch_size=batch_size, shuffle=False) #创建一个data_loader

    # Make sure the model is in eval mode.
    model.eval()
    # Define softmax function.
    softmax = nn.Softmax(dim=-1)

    # Iterate over the dataset by batches.
    for batch in tqdm(data_loader):
        img, _ = batch

        # Forward the data
        # Using torch.no_grad() accelerates the forward process.
        with torch.no_grad():
            logits = model(img.to(device))

        # Obtain the probability distributions by applying softmax on logits.
        probs = softmax(logits)

        # ---------- TODO ----------
        # Filter the data and construct a new dataset.

    # # Turn off the eval mode.
    model.train()
    return dataset

四、train部分:

# ---------- Training ----------
    # Make sure the model is in train mode before training.
    model.train() #开启train模式

    # These are used to record information in training.
    train_loss = [] #准备好记录train过程中的loss数值和accuracy的数值
    train_accs = []

    # Iterate the training set by batches.
    for batch in tqdm(train_loader): #每一个batch中进行的操作

        # A batch consists of image data and corresponding labels.
        imgs, labels = batch #从这个batch中获取到imgs数据数组 和 labels数据数组

        # Forward the data. (Make sure data and model are on the same device.)
        logits = model(imgs.to(device)) #计算出这一个batch的logits

        # Calculate the cross-entropy loss.
        # We don't need to apply softmax before computing cross-entropy as it is done automatically.
        loss = criterion(logits, labels.to(device)) #计算logits和labels之间的loss

        # Gradients stored in the parameters in the previous step should be cleared out first.
        optimizer.zero_grad() #清空之前的grad

        # Compute the gradients for parameters.
        loss.backward()

        # Clip the gradient norms for stable training.
        grad_norm = nn.utils.clip_grad_norm_(model.parameters(), max_norm=10)

        # Update the parameters with computed gradients.
        optimizer.step() #调用backward+step进行常规化的模型更新 + clip_grad_norm防止梯度爆炸

        # Compute the accuracy for current batch.
        acc = (logits.argmax(dim=-1) == labels.to(device)).float().mean()

        # Record the loss and accuracy.
        train_loss.append(loss.item()) #将这个batch的loss放到数组中
        train_accs.append(acc)     #将这个batch的acc放到数组中
    #一个epoch完成,接下来就是计算这一次的均值,然后进行打印输出
    # The average loss and accuracy of the training set is the average of the recorded values.
    train_loss = sum(train_loss) / len(train_loss) 
    train_acc = sum(train_accs) / len(train_accs)

    # Print the information.
    print(f"[ Train | {epoch + 1:03d}/{n_epochs:03d} ] loss = {train_loss:.5f}, acc = {train_acc:.5f}")

五、validation部分:

 #validtion部分和train部分基本一样,处理backward+step哪里不需要了
    # ---------- Validation ----------
    # Make sure the model is in eval mode so that some modules like dropout are disabled and work normally.
    model.eval()

    # These are used to record information in validation.
    valid_loss = []
    valid_accs = []

    # Iterate the validation set by batches.
    for batch in tqdm(valid_loader):

        # A batch consists of image data and corresponding labels.
        imgs, labels = batch

        # We don't need gradient in validation.
        # Using torch.no_grad() accelerates the forward process.
        with torch.no_grad():
          logits = model(imgs.to(device))

        # We can still compute the loss (but not the gradient).
        loss = criterion(logits, labels.to(device))

        # Compute the accuracy for current batch.
        acc = (logits.argmax(dim=-1) == labels.to(device)).float().mean()

        # Record the loss and accuracy.
        valid_loss.append(loss.item())
        valid_accs.append(acc)

    # The average loss and accuracy for entire validation set is the average of the recorded values.
    valid_loss = sum(valid_loss) / len(valid_loss)
    valid_acc = sum(valid_accs) / len(valid_accs)

    # Print the information.
    print(f"[ Valid | {epoch + 1:03d}/{n_epochs:03d} ] loss = {valid_loss:.5f}, acc = {valid_acc:.5f}")

六、test部分:

# Make sure the model is in eval mode.
# Some modules like Dropout or BatchNorm affect if the model is in training mode.
model.eval()

# Initialize a list to store the predictions.
predictions = [] #开启eval()模式后,设置一个pred数组,用于存储通过model计算得到的预测结果,之后用于和labels进行比较

# Iterate the testing set by batches.
for batch in tqdm(test_loader): #还是利用tqdm进行迭代,一个个的batch进行处理
    # A batch consists of image data and corresponding labels.
    # But here the variable "labels" is useless since we do not have the ground-truth.
    # If printing out the labels, you will find that it is always 0.
    # This is because the wrapper (DatasetFolder) returns images and labels for each batch,
    # so we have to create fake labels to make it work normally.
    imgs, labels = batch #获取图像数据

    # We don't need gradient in testing, and we don't even have labels to compute loss.
    # Using torch.no_grad() accelerates the forward process.
    with torch.no_grad():
        logits = model(imgs.to(device)) #计算得到预测的结果

    # Take the class with greatest logit as prediction and record it.
    predictions.extend(logits.argmax(dim=-1).cpu().numpy().tolist()) #直接将预测的logits转换为preditions里面一些one-hot vec

七、创建predict.csv文件,并且将prediction数组中的结果进行写入:

# Save predictions into the file.
with open("predict.csv", "w") as f: #创建一个predict.csv文件

    # The first row must be "Id, Category"
    f.write("Id,Category\n")  #第一行是:Id, Category

    # For the rest of the rows, each image id corresponds to a predicted class.
    for i, pred in  enumerate(predictions): #将predictions中的结果逐个写入到这个文件中
         f.write(f"{i},{pred}\n")

训练的结果,就算是sample的代码用T4,也要跑25分钟才能跑完80个epoch

(1)这是用sample代码跑34个epoch时的 accuracy,在train上面已经很好了,但是在valid上面还是处于50%左右

(2)在第40个epoch时,出现了突破:

之后的结果就之后再说。。。

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

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

相关文章

Support for password authentication was removed on August 13, 2021 解决方案

打开你的github&#xff0c;Setting 点击Developer settings。 点击generate new token 按照需要选择scope 生成token&#xff0c;以后复制下来。 给git设置token样式的remote url git remote set-url origin https://你的tokengithub.com/你的git用户名/仓库名称.git然后就可…

MySQL 连接查询和存储过程

一、连接查询 mysql的连接查询&#xff0c;通常都是将来自两个或多个表的记录行结合起来&#xff0c;基于这些表之间的共同字段&#xff0c;进行数据的拼接 首先&#xff0c;要确定一个主表作为结果集&#xff0c;然后将其它表的行有选择性的连接到选定的主表结果上&#xff…

算法训练day41|动态规划 part03(LeetCode343. 整数拆分、96.不同的二叉搜索树)

文章目录 343. 整数拆分思路分析代码实现 96.不同的二叉搜索树思路分析代码实现 343. 整数拆分 题目链接&#x1f525;&#x1f525; 给定一个正整数 n&#xff0c;将其拆分为至少两个正整数的和&#xff0c;并使这些整数的乘积最大化。 返回你可以获得的最大乘积。 示例 1: …

【2023研电赛】安谋科技企业命题二等奖:基于R329的AI交互早教机器人

本文为2023年第十八届中国研究生电子设计竞赛安谋科技企业命题二等奖分享&#xff0c;参加极术社区的【有奖活动】分享2023研电赛作品扩大影响力&#xff0c;更有丰富电子礼品等你来领&#xff01;&#xff0c;分享2023研电赛作品扩大影响力&#xff0c;更有丰富电子礼品等你来…

intellij idea如何查看项目maven依赖关系图

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 1、打开maven项目依赖 打开后的效果图 2、选择缩放 可以选择1:1 缩放、…

多通道振弦数据记录仪应用桥梁安全监测的解决方案

多通道振弦数据记录仪应用桥梁安全监测的解决方案 城市化进程的加快和交通运输的发展&#xff0c;桥梁作为连接城市的重要交通工具&#xff0c;其安全性也变得越来越重要。为了保证桥梁的安全性&#xff0c;需要进行定期的监测和维护。其中&#xff0c;多通道振弦数据记录仪是…

通信原理板块——窄带随机过程

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 大多数通信系统都是窄带带通型的&a…

【web开发】1、flask入门和html开发

文章目录 一、前端三剑客是什么&#xff1f;二、快速开发网站1.安装flask2.根目录下创建templates目录及web.py文件 三、HTML3.1常用标签3.2列表标签3.3表格标签3.4INPUT系列&#xff08;7个&#xff09;3.5下拉框3.6多行文本3.7案例&#xff1a;用户注册页面 一、前端三剑客是…

基于SSM的高校社团管理系统

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 随着高校社团数量的不…

Unity中神秘的Transform和transform(小写)的关系

1.为什么Transform类是保护的不能通过new 来实例化对象,也没有静态函数,而Rotate()这种方法却属于它,该如何访问? Transform 类还是被保护的不允许用户修改! protected Transform(); 是一个受保护的构造函数,不能直接实例化 Transform 类。 2.为甚么transform可以访问Tr…

解决Spring Boot启动错误的技术指南

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

Java“牵手”微店商品详情数据,微店商品详情API接口,微店API接口申请指南

微店平台商品详情接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取微店商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片等详细信息 。 获取商品详情接口API是一种用于获取电商平台上商品详情数据的接口&#xff0c;通过…

element-ui 修改tooltip样式

1.表格tooltip 统一修改 <el-table:data"tableDatas"tooltip-effect"light" .el-tooltip__popper.is-light {background: #FFF;box-shadow: 0px 0px 8px 1px rgba(0,0,0,0.16);border-radius: 4px;opacity: 1;border: none;&[x-placement^top] .p…

选择企业网盘:MCN机构如何做出明智的选择?

随着在线媒体行业的迅速发展&#xff0c;MCN机构扮演了越来越重要的角色。对于MCN机构来说&#xff0c;高效的文件管理和协作工具是必不可少的。而企业网盘正是满足这些需求的理想解决方案。 "MCN机构用什么企业网盘好&#xff1f;推荐国际云盘Zoho WorkDive和国内云盘百度…

延时消息队列

目录 前言 一、延时队列实用场景 二、DelayQueue DelayQueue的实现 使用延迟队列 DelayQueue实现延时任务的优缺点 三、RocketMQ 原理 四、Kafka 原理 实现 DelayMessage定义 消息发送代码 消费者代码 参考 前言 延时队列的内部是有序的&#xff0c;最重要的…

stm32之31.iic

iic双线制。一根是SCL&#xff0c;作为时钟同步线;一根是SDA&#xff0c;作为数据传输线 SDN #include "iic.h"#define SCL PBout(8)#define SDA_W PBout(9) #define SDA_R PBin(9)void IIC_GPIOInit(void) {GPIO_InitTypeDef GPIO_InitStructure;//使能时钟GR…

开始投简历了

歇了好长时间&#xff0c;也该开始找点事情折腾了。 第一周基本上是没有什么太多的消息&#xff0c;大部分情况就是收到回复的邮件说你很优秀&#xff0c;希望下次合作这种礼节性的拒绝邮件。 给人有点感觉都是在忽悠&#xff0c;有点感觉现在的公司一边到处拒绝&#xff0c;…

短信软件平台搭建最新客户端|移讯云短信系统

根据客户 和市场需要 增加了新的客户端 新的客户端客户登录后发送短信时可自行选择用哪个通道来进行发送短信。每个通道的充值数量不一样。 通过后台给客户分配可使用的通道&#xff0c;只有在后台给客户分配可使用的通道后客户在登录客户端发送短信时才可进行选择。 关于客…

NoSQL之 Redis介绍与配置

目录 一、关系数据库和非关系数据库概述 1、关系型数据库 2、非关系型数据库 二、关系数据库和非关系数据库的区别 1、数据存储格式不同 2、扩展方式不同 3、对事务的支持不同 三、非关系数据库产生背景 1、总结 四、Redis简介 1、 Redis的单线程模式 2、Redis优点…

ChatGPT AIGC 完成多维分析雷达图

我们先让ChatGPT来帮我们总结一下多维分析雷达图的功能与作用。 同样ChatGPT AIGC完成的动态雷达图效果如下; 这样的一个多维分析动态雷达图是用HTML,JS,Echarts 来完成的。 将完整代码复制如下: <!DOCTYPE html> <html style="height: 100%"><h…