目录

一、开发背景

二、网络结构

三、模型特点

四、代码实现

1. model.py

2. train.py

3. predict.py

4. spilit_data.py

五、参考内容


一、开发背景

AlexNet由Hinton和他的学生Alex Krizhevsky设计,模型名字来源于论文第一作者的姓名Alex。该模型以很大的优势获得了2012年ISLVRC竞赛的冠军网络,分类准确率由传统的 70%+提升到 80%+,自那年之后,深度学习开始迅速发展。

ImageNet是一个在2009年创建的图像数据集,从2010年开始到2017年举办了七届的ImageNet 挑战赛——ImageNet Large Scale Visual Recognition ChallengeI (LSVRC),在这个挑战赛上诞生了AlexNet、ZFNet、OverFeat、VGG、Inception、ResNet、WideResNet、FractalNet、DenseNet、ResNeXt、DPN、SENet 等经典模型。

二、网络结构

Alexnet模型为8层深度网络,由5个卷积层和3个全连接层构成,不计LRN层和池化层。AlexNet 跟 LeNet 结构类似,但使用了更多的卷积层和更大的参数空间来拟合大规模数据集 ImageNet。它是浅层神经网络和深度神经网络的分界线,如下图所示:

网络详解:AlexNet网络结构详解(含各层维度大小计算过程)与PyTorch实现

三、模型特点

  1. 使用CUDA加速深度卷积网络的训练,利用GPU强大的并行计算能力,处理神经网络训练时大量的矩阵运算;
  2. 使用大数据训练,是百万级ImageNet图像数据,提升算法的准确率,避免过拟合;
  3. 使用ReLU作为激活函数,解决了SIgmoid在网络较深时的梯度消失问题,使收敛更快;
  4. 使用随机丢弃技术(dropout)以0.5的概率选择性地将隐藏层神经元的输出设置为零,以这种方式“dropped out”的神经元既不参与前向传播,也不参与反向传播,避免模型的过拟合;
  5. 重叠最大池化(overlapping max pooling),池化的步长小于核尺寸,使得输出之间会有重叠和覆盖,提升了特征的丰富性,并且避免平均池化的模糊化效果;
  6. 使用 LRN 局部响应归一化(Local Response Normalization)层,对局部神经元的活动创建竞争机制,使得响应较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力,使准确率更高;
  7. 进行数据增强,随机从256*256的原始图像中截取224*224大小的区域(以及水平翻转的镜像),相当于增强了(256-224)*(256-224)*2=2048倍的数据量,减轻过拟合,提升泛化能力。

为什么Dropout有效?

Dropout背后理念和集成模型很相似。在Drpout层,不同的神经元组合被关闭,这代表了一种不同的结构,所有这些不同的结构使用一个的子数据集并行地带权重训练,而权重总和为1。如果Dropout层有 n 个神经元,那么会形成 2n 个不同的子结构。在预测时,相当于集成这些模型并取均值。这种结构化的模型正则化技术有利于避免过拟合。Dropout有效的另外一个视点是:由于神经元是随机选择的,所以可以减少神经元之间的相互依赖,从而确保提取出相互独立的重要特征。

四、代码实现

  • model.py :定义AlexNet网络模型
  • train.py:加载数据集并训练,计算loss和accuracy,保存训练好的网络参数
  • predict.py:用自己的数据集进行分类测试
  • spilit_data.py:划分给定的数据集为训练集和测试集

注意:代码实现没有还原两个小型GPU同时运算的设计特点,而是在一个模型中运行

1. model.py

# 导入pytorch库
import torch
# 导入torch.nn模块
from torch import nn
# nn.functional:(一般引入后改名为F)有各种功能组件的函数实现,如:F.conv2d
import torch.nn.functional as F# 定义AlexNet网络模型
# MyLeNet5(子类)继承nn.Module(父类)
class MyAlexNet(nn.Module):# 子类继承中重新定义Module类的__init__()和forward()函数# init():进行初始化,申明模型中各层的定义def __init__(self):# super:引入父类的初始化方法给子类进行初始化super(MyAlexNet, self).__init__()# 卷积层,输入大小为224*224,输出大小为55*55,输入通道为3,输出为96,卷积核为11,步长为4self.c1 = nn.Conv2d(in_channels=3, out_channels=96, kernel_size=11, stride=4, padding=2)# 使用ReLU作为激活函数self.ReLU = nn.ReLU()# MaxPool2d:最大池化操作# 最大池化层,输入大小为55*55,输出大小为27*27,输入通道为96,输出为96,池化核为3,步长为2self.s1 = nn.MaxPool2d(kernel_size=3, stride=2)# 卷积层,输入大小为27*27,输出大小为27*27,输入通道为96,输出为256,卷积核为5,扩充边缘为2,步长为1self.c2 = nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, stride=1, padding=2)# 最大池化层,输入大小为27*27,输出大小为13*13,输入通道为256,输出为256,池化核为3,步长为2self.s2 = nn.MaxPool2d(kernel_size=3, stride=2)# 卷积层,输入大小为13*13,输出大小为13*13,输入通道为256,输出为384,卷积核为3,扩充边缘为1,步长为1self.c3 = nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, stride=1, padding=1)# 卷积层,输入大小为13*13,输出大小为13*13,输入通道为384,输出为384,卷积核为3,扩充边缘为1,步长为1self.c4 = nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, stride=1, padding=1)# 卷积层,输入大小为13*13,输出大小为13*13,输入通道为384,输出为256,卷积核为3,扩充边缘为1,步长为1self.c5 = nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, stride=1, padding=1)# 最大池化层,输入大小为13*13,输出大小为6*6,输入通道为256,输出为256,池化核为3,步长为2self.s5 = nn.MaxPool2d(kernel_size=3, stride=2)# Flatten():将张量(多维数组)平坦化处理,神经网络中第0维表示的是batch_size,所以Flatten()默认从第二维开始平坦化self.flatten = nn.Flatten()# 全连接层# Linear(in_features,out_features)# in_features指的是[batch_size, size]中的size,即样本的大小# out_features指的是[batch_size,output_size]中的output_size,样本输出的维度大小,也代表了该全连接层的神经元个数self.f6 = nn.Linear(6*6*256, 4096)self.f7 = nn.Linear(4096, 4096)# 全连接层&softmaxself.f8 = nn.Linear(4096, 1000)self.f9 = nn.Linear(1000, 2)# forward():定义前向传播过程,描述了各层之间的连接关系def forward(self, x):x = self.ReLU(self.c1(x))x = self.s1(x)x = self.ReLU(self.c2(x))x = self.s2(x)x = self.ReLU(self.c3(x))x = self.ReLU(self.c4(x))x = self.ReLU(self.c5(x))x = self.s5(x)x = self.flatten(x)x = self.f6(x)# Dropout:随机地将输入中50%的神经元激活设为0,即去掉了一些神经节点,防止过拟合# “失活的”神经元不再进行前向传播并且不参与反向传播,这个技术减少了复杂的神经元之间的相互影响x = F.dropout(x, p=0.5)x = self.f7(x)x = F.dropout(x, p=0.5)x = self.f8(x)x = F.dropout(x, p=0.5)x = self.f9(x)return x# 每个python模块(python文件)都包含内置的变量 __name__,当该模块被直接执行的时候,__name__ 等于文件名(包含后缀 .py )
# 如果该模块 import 到其他模块中,则该模块的 __name__ 等于模块名称(不包含后缀.py)
# “__main__” 始终指当前执行模块的名称(包含后缀.py)
# if确保只有单独运行该模块时,此表达式才成立,才可以进入此判断语法,执行其中的测试代码,反之不行
if __name__ == '__main__':# rand:返回一个张量,包含了从区间[0, 1)的均匀分布中抽取的一组随机数,此处为四维张量x = torch.rand([1, 3, 224, 224])# 模型实例化model = MyAlexNet()y = model(x)

2. train.py

import torch
from torch import nn
from model import MyAlexNet
from torch.optim import lr_scheduler
from torchvision import transforms
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader
import os
import matplotlib.pyplot as plt# 解决中文显示问题
# 运行配置参数中的字体(font)为黑体(SimHei)
plt.rcParams['font.sans-serif'] = ['simHei']
# 运行配置参数总的轴(axes)正常显示正负号(minus)
plt.rcParams['axes.unicode_minus'] = FalseROOT_TRAIN = 'D:/pycharm/AlexNet/data/train'
ROOT_TEST = 'D:/pycharm/AlexNet/data/val'# 将图像的像素值归一化到[-1,1]之间
normalize = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])# Compose():将多个transforms的操作整合在一起
train_transform = transforms.Compose([# Resize():把给定的图像随机裁剪到指定尺寸transforms.Resize((224, 224)),# RandomVerticalFlip():以0.5的概率竖直翻转给定的PIL图像transforms.RandomVerticalFlip(),# ToTensor():数据转化为Tensor格式transforms.ToTensor(),normalize])val_transform = transforms.Compose([transforms.Resize((224, 224)),transforms.ToTensor(),normalize])# 加载训练数据集
# ImageFolder:假设所有的文件按文件夹保存,每个文件夹下存储同一个类别的图片,文件夹名为类名,其构造函数如下:
# ImageFolder(root, transform=None, target_transform=None, loader=default_loader)
# root:在root指定的路径下寻找图像,transform:对输入的图像进行的转换操作
train_dataset = ImageFolder(ROOT_TRAIN, transform=train_transform)
# DataLoader:将读取的数据按照batch size大小封装给训练集
# dataset (Dataset):加载数据的数据集
# batch_size (int, optional):每个batch加载多少个样本(默认: 1)
# shuffle (bool, optional):设置为True时会在每个epoch重新打乱数据(默认: False)
train_dataloader = DataLoader(train_dataset, batch_size=32, shuffle=True)# 加载训练数据集
val_dataset = ImageFolder(ROOT_TEST, transform=val_transform)
val_dataloader = DataLoader(val_dataset, batch_size=32, shuffle=True)# 如果有NVIDA显卡,可以转到GPU训练,否则用CPU
device = 'cuda' if torch.cuda.is_available() else 'cpu'# 模型实例化,将模型转到device
model = MyAlexNet().to(device)# 定义损失函数(交叉熵损失)
loss_fn = nn.CrossEntropyLoss()# 定义优化器(随机梯度下降法)
# params(iterable):要训练的参数,一般传入的是model.parameters()
# lr(float):learning_rate学习率,也就是步长
# momentum(float, 可选):动量因子(默认:0),矫正优化率
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)# 学习率每隔10轮变为原来的0.5
# StepLR:用于调整学习率,一般情况下会设置随着epoch的增大而逐渐减小学习率从而达到更好的训练效果
# optimizer (Optimizer):更改学习率的优化器
# step_size(int):每训练step_size个epoch,更新一次参数
# gamma(float):更新lr的乘法因子
lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)# 定义训练函数
def train(dataloader, model, loss_fn, optimizer):loss, current, n = 0.0, 0.0, 0# dataloader: 传入数据(数据包括:训练数据和标签)# enumerate():用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在for循环当中# enumerate返回值有两个:一个是序号,一个是数据(包含训练数据和标签)# x:训练数据(inputs)(tensor类型的),y:标签(labels)(tensor类型)for batch, (x, y) in enumerate(dataloader):# 前向传播image, y = x.to(device), y.to(device)# 计算训练值output = model(image)# 计算观测值(label)与训练值的损失函数cur_loss = loss_fn(output, y)# torch.max(input, dim)函数# input是具体的tensor,dim是max函数索引的维度,0是每列的最大值,1是每行的最大值输出# 函数会返回两个tensor,第一个tensor是每行的最大值;第二个tensor是每行最大值的索引_, pred = torch.max(output, axis=1)# 计算每批次的准确率# output.shape[0]为该批次的多少,output的一维长度# torch.sum()对输入的tensor数据的某一维度求和cur_acc = torch.sum(y == pred)/output.shape[0]# 反向传播# 清空过往梯度optimizer.zero_grad()# 反向传播,计算当前梯度cur_loss.backward()# 根据梯度更新网络参数optimizer.step()# item():得到元素张量的元素值loss += cur_loss.item()current += cur_acc.item()n = n + 1train_loss = loss / ntrain_acc = current / n# 计算训练的错误率print('train_loss==' + str(train_loss))# 计算训练的准确率print('train_acc' + str(train_acc))return train_loss, train_acc# 定义验证函数
def val(dataloader, model, loss_fn):loss, current, n = 0.0, 0.0, 0# eval():如果模型中有Batch Normalization和Dropout,则不启用,以防改变权值model.eval()with torch.no_grad():for batch, (x, y) in enumerate(dataloader):# 前向传播image, y = x.to(device), y.to(device)output = model(image)cur_loss = loss_fn(output, y)_, pred = torch.max(output, axis=1)cur_acc = torch.sum(y == pred)/output.shape[0]loss += cur_loss.item()current += cur_acc.item()n = n+1val_loss = loss / nval_acc = current / n# 计算验证的错误率print('val_loss=' + str(val_loss))# 计算验证的准确率print('val_acc=' + str(val_acc))return val_loss, val_acc# 定义画图函数
# 错误率
def matplot_loss(train_loss, val_loss):# 参数label = ''传入字符串类型的值,也就是图例的名称plt.plot(train_loss, label='train_loss')plt.plot(val_loss, label='val_loss')# loc代表了图例在整个坐标轴平面中的位置(一般选取'best'这个参数值)plt.legend(loc='best')plt.xlabel('loss')plt.ylabel('epoch')plt.title("训练集和验证集的loss值对比图")plt.show()# 准确率
def matplot_acc(train_acc, val_acc):plt.plot(train_acc, label = 'train_acc')plt.plot(val_acc, label = 'val_acc')plt.legend(loc = 'best')plt.xlabel('acc')plt.ylabel('epoch')plt.title("训练集和验证集的acc值对比图")plt.show()#开始训练
loss_train = []
acc_train = []
loss_val = []
acc_val = []# 训练次数
epoch = 20
# 用于判断最佳模型
min_acc = 0
for t in range(epoch):lr_scheduler.step()print(f"epoch{t+1}\n----------")# 训练模型train_loss, train_acc = train(train_dataloader, model, loss_fn, optimizer)# 验证模型val_loss, val_acc = val(val_dataloader, model, loss_fn)loss_train.append(train_loss)acc_train.append(train_acc)loss_val.append(val_loss)acc_val.append(val_acc)# 保存最好的模型权重if val_acc > min_acc:folder = 'save_model'# path.exists:判断括号里的文件是否存在的意思,括号内可以是文件路径,存在为Trueif not os.path.exists(folder):# os.mkdir() 方法用于以数字权限模式创建目录os.mkdir('save_model')min_acc = val_accprint(f"save best model,第{t+1}轮")# torch.save(state, dir):保存模型等相关参数,dir表示保存文件的路径+保存文件名# model.state_dict():返回的是一个OrderedDict,存储了网络结构的名字和对应的参数torch.save(model.state_dict(), 'save_model/best_model.pth')# 保存最后一轮权重if t == epoch-1:torch.save(model.state_dict(), 'save_model/best_model.pth')matplot_loss(loss_train, loss_val)
matplot_acc(acc_train, acc_val)print('done')

3. predict.py

import torch
from model import MyAlexNet
from torch.autograd import Variable
from torchvision import transforms
from torchvision.transforms import ToPILImage
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoaderROOT_TRAIN = 'D:/pycharm/AlexNet/data/train'
ROOT_TEST = 'D:/pycharm/AlexNet/data/val'# 将图像的像素值归一化到[-1,1]之间
normalize = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])val_transform = transforms.Compose([transforms.Resize((224, 224)),transforms.ToTensor(),normalize
])# 加载训练数据集
val_dataset = ImageFolder(ROOT_TEST, transform=val_transform)# 如果有NVIDA显卡,转到GPU训练,否则用CPU
device = 'cuda' if torch.cuda.is_available() else 'cpu'# 模型实例化,将模型转到device
model = MyAlexNet().to(device)# 加载train.py里训练好的模型
model.load_state_dict(torch.load(r'D:\pycharm\AlexNet\save_model\best_model.pth'))# 结果类型
classes = ["cat","dog"
]# 把Tensor转化为图片,方便可视化
show = ToPILImage()# 进入验证阶段
model.eval()
for i in range(1):x, y = val_dataset[i][0], val_dataset[i][1]# show():显示图片show(x).show()# torch.unsqueeze(input, dim),input(Tensor):输入张量,dim (int):插入维度的索引,最终扩展张量维度为4维x = Variable(torch.unsqueeze(x, dim=0).float(), requires_grad=False).to(device)with torch.no_grad():pred = model(x)# argmax(input):返回指定维度最大值的序号# 得到预测类别中最高的那一类,再把最高的这一类对应classes中的那一类predicted, actual = classes[torch.argmax(pred[0])], classes[y]# 输出预测值与真实值print(f'predicted:"{predicted}", actual:"{actual}"')

4. spilit_data.py

import os
from shutil import copy
import random# 如果file不存在,创建file
def mkfile(file):if not os.path.exists(file):os.makedirs(file)# 获取data文件夹下所有除.txt文件以外所有文件夹名(即需要分类的类名)
# os.listdir():用于返回指定的文件夹包含的文件或文件夹的名字的列表
file_path = 'D:/pycharm/AlexNet/data_name'
pet_class = [cla for cla in os.listdir(file_path) if ".txt" not in cla]# 创建训练集train文件夹,并由类名在其目录下创建子目录
mkfile('data/train')
for cla in pet_class:mkfile('data/train/' + cla)# 创建验证集val文件夹,并由类名在其目录下创建子目录
mkfile('data/val')
for cla in pet_class:mkfile('data/val/' + cla)# 划分比例,训练集 : 验证集 = 8 : 2
split_rate = 0.2# 遍历所有类别的图像并按比例分成训练集和验证集
for cla in pet_class:# 某一类别的子目录cla_path = file_path + '/' + cla + '/'# iamges列表存储了该目录下所有图像的名称images = os.listdir(cla_path)num = len(images)# 从images列表中随机抽取k个图像名称# random.sample:用于截取列表的指定长度的随机数,返回列表# eval_index保存验证集val的图像名称eval_index = random.sample(images, k=int(num * split_rate))for index, image in enumerate(images):if image in eval_index:image_path = cla_path + imagenew_path = 'data/val/' + cla# copy():将源文件的内容复制到目标文件或目录copy(image_path, new_path)# 其余图像保存在训练集train中else:image_path = cla_path + imagenew_path = 'data/train/' + clacopy(image_path, new_path)# '\r' 回车,回到当前行的行首,而不会换到下一行,如果接着输出,本行以前的内容会被逐一覆盖# <模板字符串>.format(<逗号分隔的参数>)# end="":将print自带的换行用end中指定的str代替print("\r[{}] processing [{}/{}]".format(cla, index + 1, num), end="")print()print("processing done!")

五、参考内容

1. 文章

《ImageNet Classification with Deep Convolutional Neural Networks》http://www.cs.toronto.edu/~fritz/absps/imagenet.pdf

2. 视频

从0开始撸代码--手把手教你搭建AlexNet网络模型训练自己的数据集(猫狗分类)https://www.bilibili.com/video/BV18L4y167jr?p=4&vd_source=78dedbc0ab33a4edb884e1ef98f3c6b8

AlexNet代码(超详细注释)+数据集下载地址:

https://download.csdn.net/download/qq_43307074/86730471

CNN经典网络模型(二):AlexNet简介及代码实现(PyTorch超详细注释版)相关推荐

  1. CNN经典网络模型(四):GoogLeNet简介及代码实现(PyTorch超详细注释版)

    目录 一.开发背景 二.网络结构 三.模型特点 四.代码实现 1. model.py 2. train.py 3. predict.py 4. spilit_data.py 五.参考内容 一.开发背景 ...

  2. SENet代码复现+超详细注释(PyTorch)

    在卷积网络中通道注意力经常用到SENet模块,来增强网络模型在通道权重的选择能力,进而提点.关于SENet的原理和具体细节,我们在上一篇已经详细的介绍了:经典神经网络论文超详细解读(七)--SENet ...

  3. ResNeXt代码复现+超详细注释(PyTorch)

    ResNeXt就是一种典型的混合模型,由基础的Inception+ResNet组合而成,本质在gruops分组卷积,核心创新点就是用一种平行堆叠相同拓扑结构的blocks代替原来 ResNet 的三层 ...

  4. CNN经典网络模型:LeNet,Alexnet,VGGNet,GoogleNet,ReSNet

    关于卷积神经网络CNN,网络和文献中有非常多的资料,我在工作/研究中也用了好一段时间各种常见的model了,就想着简单整理一下,以备查阅之需.如果读者是初接触CNN,建议可以先看一看"Dee ...

  5. CNN经典网络模型详解LeNet(LeNet-1, LeNet-4, LeNet-5最详细, Boosted LeNet-4)发展和过程

    1.LeNet-5详细 2.回顾LeNet其他模型结构 3.补充 一.LeNet-5详细 LeNet-5是最受欢迎的LeNet-5,与LeNet-4相比只有细微差别,32x32的输入图像 – 6个28 ...

  6. Pyside2 学习系列二:PyInstaller打包项目exe (超详细的Pyside2 攻略)

    继上一篇文章创建了项目后,本章我们进行项目的打包工作. 本项目的所有演示代码:github可在这里下载. 打包只用的工具为PyInstaller. 打包步骤 1 准备环境 1.1 安装`PyInsta ...

  7. 经典剪枝算法的例题——Sticks详细注释版

    这题听说是道十分经典的剪枝算的题目,不要问我剪枝是什么,我也不知道,反正我只知道用到了深度搜索 我参考了好多资料才悟懂,然后我发现网上的那些大神原理讲的很明白,但代码没多少注释,看的很懵X,于是我抄起 ...

  8. CNN经典网络模型综述及发散思考(LeNet/ AlexNet/VGGNet/GoogLeNet/ResNet)

    目录 一. 背景 成功原因 设计目标 二. 经典网络模型 LeNet(1990 年) 网络特点 AlexNet(2012年) 网络特点 VGGNet(2014年) 网络特点 发散思考 GoogLeNe ...

  9. 手撕 CNN 经典网络之 AlexNet(理论篇)

    大家好,我是红色石头! 第一个典型的CNN是LeNet5网络,而第一个大放异彩的CNN却是AlexNet.2012年在全球知名的图像识别竞赛 ILSVRC 中,AlexNet 横空出世,直接将错误率降 ...

最新文章

  1. 让人脸识别算法失灵,还能抵抗微信微博照片压缩!武大Adobe提出抗压缩对抗新框架,成功率最高超90%...
  2. Vue 全家桶 + Express 实现的博客(后端API全部自己手写)
  3. 边框回归的损失函数_分类损失函数,边框回归损失函数
  4. c++ for each 遍历tuple
  5. 使用acme.sh签发Let's Encrypt的免费数字证书
  6. python怎么用圆周率_Python语言中,如何使用圆周率?
  7. 外卖员不满上楼送餐要求向外卖吐口水4次,顾客不知情吃下整份外卖...
  8. 三分钟带你弄懂slot插槽——vue进阶
  9. python自动化客户端_python 在 nwjs 应用客户端做 UI 自动化
  10. PetShop 4.0 官方详解
  11. 2016峰会:项目管理与高级项目管理(广州站)
  12. 安卓app执行linux命令,如何在android程序中执行adb shell指令
  13. 在计算机上没有找到w3svc_困扰数学家90年的猜想,被计算机搜索30分钟解决了
  14. MedPy医学图像评价的python库
  15. Flash计时器次数为什么设置1次的重要性
  16. Redis下载与安装(Linux版)
  17. STM32入门开发: LWIP网络协议栈移植(网卡采用DM9000)
  18. 抖音小店开通及保证金详情;抖音小店商品上架详细流程。丨国仁网络资讯
  19. 微信开发工具出现 [渲染层网络层错误]
  20. java穷举密码_Java实现穷举密码登录FTP服务器

热门文章

  1. Git环境搭建与使用说明
  2. 计算机室内设计 cad 论文,CAD室内设计毕业答辩论文.doc
  3. 【附源码】计算机毕业设计SSM校园考研互助网站
  4. python做pca图_【教程】组学研究,用python快速实现PCA分析和绘图
  5. 生于数据中心的超融合,正向边缘进击
  6. 【Java学习笔记】【Day3】面向对象及其特点、三个关键字
  7. 深度学习tensorflow之手势识别历史使用
  8. Win11设置定时关机的方法
  9. Intel x86/x64 编程手册-开发文档
  10. 家居调查:儿童家具为何不标明重金属含量