pytorch实战10:基于pytorch简单实现CGAN

news2024/11/30 2:45:16

基于pytorch简单实现CGAN

前言

​ 最近在看经典的卷积网络架构,打算自己尝试复现一下,在此系列文章中,会参考很多文章,有些已经忘记了出处,所以就不贴链接了,希望大家理解。

​ 完整的代码在最后。

本系列必须的基础

​ python基础知识、CNN原理知识、pytorch基础知识

本系列的目的

​ 一是帮助自己巩固知识点;

​ 二是自己实现一次,可以发现很多之前的不足;

​ 三是希望可以给大家一个参考。

目录结构

文章目录

    • 基于pytorch简单实现CGAN
      • 1. 前言:
      • 2. 数据介绍与数据加载器:
      • 3. 生成器:
      • 4. 判别器:
      • 5. 训练:
      • 6. 训练结果展示与思考:
      • 7. 预测:
      • 8. 总结:

1. 前言:

​ 今天来实现一下‘干’系列中的CGAN,相比于原始的GAN来说,CGAN更加稳定,且引入了条件参数,可以在一定程度上控制输出。

​ 本次实现CGAN,使用pytorch库和手写数字数据集MNIST(注定了很简单的)。另外,本次的目的是简单实现一下,更深入的优化,就靠大家了。

​ 最后,补充一下我的项目结构:(特别注意:我的数据集文件夹在其它文件夹内

├─fake_images
|  └─ 用于保存生成器生成的图片的文件夹
├─network_files
│  └─ 用于存放网络结构的文件夹
├─save_weights
|  └─ 用于存放训练好权重的文件夹
|_ 数据加载器、训练、测试文件

2. 数据介绍与数据加载器:

数据介绍

​ MNIST数据集大家应该都不陌生吧。我这里简单说明一下,这个数据集是灰度图(通道数为1),所有图片大小为28*28,内容为0-9的数字。

在这里插入图片描述

数据加载器

​ 这里不需要像前面一样,实现复杂的数据加载器。因为官方已经为我们封装好了现成的加载器,这里我们只是将之封装到文件的函数中即可,如下:

from torchvision import transforms as T
from torchvision.datasets import MNIST

# 这个简单,MNIST数据集很经典,因此可以直接调用官方的方法
def My_Dataset(train=True):
    # 定义预处理方法
    transforms = T.Compose([
        T.Resize(28), # 这个其实不需要,只是后期如果要改为其它图片,肯行需要修改
        T.ToTensor(),
        T.Normalize([0.5], [0.5])
    ])
    if train:
        #  路径需要修改为自己的
        # 另外,这个数据集会自动下载
        data = MNIST('../data/mnist',train=True,download=True,transform=transforms)
    else:
        data = MNIST('../data/mnist', train=False, download=True, transform=transforms)
    return data

3. 生成器:

​ 生成器很好定义,只需要注意最后的输出为图像尺寸即可。关于生成器的具体结构,其实论文中并没有给出,但是网上有别人实现的架构,我们可以拿来用:(下图来自GitHub项目pytorch-MNIST-CelebA-cGAN-cDCGAN

在这里插入图片描述

​ 其中,上图中G输出784,是因为MNIST图像大小为28*28=784。另外,上图中z是输入的噪声向量,y为其对应的标签。

​ 按照上图架构,可以实现生成器:

import torch
from torch import nn
import numpy as np

class Generator(nn.Module):
    def __init__(self,in_dim,img_shape,num_classes=10):
        '''
        :param in_dim: 噪声的维度
        :param num_classes: 类别个数,为数字0-9,即10个类别
        :param img_shape: 生成的图片尺寸,一般与真实图片一致,即28*28
        '''
        super(Generator, self).__init__()
        # 初始化
        self.in_dim = in_dim
        self.num_classes = num_classes
        self.img_shape = img_shape
        self.label_embedding = nn.Embedding(self.num_classes,self.num_classes)

        # 定义模型
        self.model = nn.Sequential(
            # 不要忘记输入是真实标签+噪声
            self.block(self.in_dim+self.num_classes,128,normalize=False),
            self.block(128,256),
            self.block(256,512),
            self.block(512,1024),
            #np.prod 输入a是数组,返回指定轴上的乘积,不指定轴默认是所有元素的乘积
            nn.Linear(1024,int(np.prod(self.img_shape))),
            nn.Tanh(),
        )

    # 构建基础块
    def block(self,in_channels,out_channels,normalize=True):
        layers = []
        layers.append(nn.Linear(in_channels,out_channels))
        # 是否初始化
        if normalize:
            layers.append(nn.BatchNorm1d(out_channels))
        # 不要忘记加上激活函数
        layers.append(nn.LeakyReLU(0.2,inplace=True))
        return nn.Sequential(*layers)

    def forward(self,noise,labels):
        # 将输入标签和噪声拼接在一起
        # temp2 = labels # 256
        # temp = self.label_embedding(labels) # torch.Size([256, 10])
        # 将标签转为了向量,目的是为和噪声进行拼接
        input_x = torch.cat((self.label_embedding(labels),noise),-1) # torch.Size([256, 110])
        # 送入模型
        output_img = self.model(input_x)
        # 将输出构造成真实图片的shape信息【batch,w,h】
        output_img = output_img.view(output_img.size(0),*(self.img_shape))
        return output_img

4. 判别器:

​ 同样根据上图实现判别器,只是注意最后的输出为1个通道,这是因为我们同时输入了图像和对应标签,最后就直接输出一个概率值,这个概率值越接近1,表示判别其为真实图像,越接近0表示假图像

​ 同样,根据上图写出判别器架构,具体可以看注释:

import numpy as np
import torch.nn as nn
import torch

class Discriminator(nn.Module):
    def __init__(self,img_shape,num_classes=10):
        '''
        :param img_shape: 图像尺寸,这里为28*28
        :param num_classes: 类别数,即10类
        '''
        super(Discriminator, self).__init__()
        # 初始化一下
        self.num_classes = num_classes
        self.img_shape = img_shape
        self.label_embedding = nn.Embedding(self.num_classes,self.num_classes)
        # 定义模型
        self.model = nn.Sequential(
            nn.Linear(self.num_classes+int(np.prod(self.img_shape)),512),
            nn.LeakyReLU(0.2,inplace=True),
            nn.Dropout(0.5),
            nn.Linear(512,512),
            nn.Dropout(0.5),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(512,512),
            nn.Dropout(0.5),
            nn.LeakyReLU(0.2, inplace=True),
            # 最后输出为1,表示分类
            nn.Linear(512,1)
        )

    def forward(self,img,labels):
        input_x = torch.cat((img.view(img.size(0), -1), self.label_embedding(labels)),-1)
        output_class = self.model(input_x)
        return output_class

5. 训练:

​ 相比于网络结构的定义,CGAN如何训练的,更重要。

​ 首先,导入基本的包和我们自己定义的数据加载器、网络结构等:

import os
from My_Dataset import My_Dataset
from network_files.Discriminator import Discriminator as D
from network_files.Generator import Generator as G

import numpy as np
import torch
from torch import nn
from torch.utils.data import DataLoader
from torch.autograd import Variable
from torch import optim
from torchvision.utils import save_image # 用于生成图片、保持图片

​ 接着,定义基本的参数:

# 定义基本参数
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') # 设备
batch_size = 256 # batch大小
epoch = 400 # 训练批次
lr = 0.002 # 初始学习率
img_shape = (1,28,28) # 图像shape
num_classes = 10 # 类别数目
in_dim = 100 # 噪声维度

​ 然后,创建模型、定义优化器(Adam)、损失函数(MSE)并加载数据,这都是基本操作:

# 创建模型
Generator = G(in_dim,img_shape,num_classes)
Discriminator = D(img_shape,num_classes)
Generator.to(device)
Discriminator.to(device)
# 定义损失函数
loss = nn.MSELoss()
# 优化器
optim_G = optim.Adam(Generator.parameters(),lr=lr,betas=(0.5, 0.999))
optim_D = optim.Adam(Discriminator.parameters(),lr=lr,betas=(0.5, 0.999))
# 加载数据
train_dataset = My_Dataset(train=True)
train_loader = DataLoader(train_dataset,batch_size=batch_size,shuffle=True,drop_last=True)

​ 接着开始训练:

# 开始训练
for e in range(epoch):

​ 首先,训练到一定程度时调整学习率,这个方法也是我最近看到的,感觉可以很方便的实现调整学习率:

# 开始训练
for e in range(epoch):
    #学习率调整
    if (e + 1) == 100:
        optim_D.param_groups[0]['lr'] /= 10
        optim_G.param_groups[0]['lr'] /= 10
        print("learning rate change!")

        if (e + 1) == 250:
            optim_D.param_groups[0]['lr'] /= 10
            optim_G.param_groups[0]['lr'] /= 10
            print("learning rate change!")

​ 然后,开始训练第一个epoch,首先把数据集放入GPU中:

# 开始训练
for e in range(epoch):
    #学习率调整
    ...... # 这里不重复了,直接省略  
    for i,(batch_img,batch_label) in enumerate(train_loader):
        # 放入设备中
        batch_img,batch_label = batch_img.to(device),batch_label.to(device)

​ 接着,需要创建噪声向量和其对应的标签,并创建两个全为0和全为1的向量,用于后期与判别器输出比较(因为判别器只输出一个概率值,因此需要创建这两个向量):

# 开始训练
for e in range(epoch):
    #学习率调整
    ...... # 这里不重复了,直接省略  
    for i,(batch_img,batch_label) in enumerate(train_loader):
        # 放入设备中
        batch_img,batch_label = batch_img.to(device),batch_label.to(device)
		# 创建噪声向量和标签
        # z = [batch,100] , labels = [64] 为要生成的数字图片标签
        z = Variable(torch.cuda.FloatTensor(np.random.normal(0, 1, (batch_size, in_dim))))  # 噪声样本
        g_labels = Variable(torch.cuda.LongTensor(np.random.randint(0, num_classes, batch_size)))  # 对应的标签
        #  用于计算损失的标签,一个全为1,一个全为0
        valid = Variable(torch.cuda.FloatTensor(batch_size, 1).fill_(1.0), requires_grad=False)  # torch.Size([64, 1])
        fake = Variable(torch.cuda.FloatTensor(batch_size, 1).fill_(0.0), requires_grad=False)  # torch.Size([64, 1])

​ 然后,开始真正的训练,训练的思路如下:

在这里插入图片描述

​ 按照上述思路,实现训练过程:

# 开始训练
for e in range(epoch):
    #学习率调整
    ...... # 这里不重复了,直接省略  
    for i,(batch_img,batch_label) in enumerate(train_loader):
        ...... # 前面那些基础的定义
        # 生成器根据噪声和标签生成假的图片
        optim_G.zero_grad()
        g_img = Generator(z,g_labels)
        # 判别器判别,输出的为[batch,1],输出的是概率值,越接近1,表示生成的图片越真实
        g_class = Discriminator(g_img,g_labels)
        # 损失
        g_loss = loss(g_class,valid)
        g_loss.backward()
        optim_G.step()
        # 训练判别器
        optim_D.zero_grad()
        # 实图片的损失,目的就是希望其与真实标签1的损失最小
        d_real_class = Discriminator(batch_img,batch_label)
        d_real_loss = loss(d_real_class,valid)
        # g_img.detach() 不要忘记detach,不然会报错的,因为前面backward已经释放了
        # 生成的虚假图片的损失,目的就是希望其与虚假标签0的损失最小
        d_fake_class = Discriminator(g_img.detach(),g_labels)
        d_fake_loss = loss(d_fake_class,fake)
        all_loss = (d_fake_loss+d_real_loss)/2
        all_loss.backward()
        optim_D.step()
        # 每个step打印损失
        print('epoch{%d},batch{%d},D_loss: %.5f,G_loss: %.5f' % (e+1,i+1,all_loss.item(),g_loss.item()))

​ 为了能够看到训练的效果,在每经历一个epoch后,都用生成器去生成图像,并保持到指定路径(需要自己修改):

# 开始训练
for e in range(epoch):
    #学习率调整
    ...... # 这里不重复了,直接省略  
    for i,(batch_img,batch_label) in enumerate(train_loader):
		...... # 整个训练过程省略
	# 每一个epoch,看看生成器生成的图片咋样了
    z = Variable(torch.cuda.FloatTensor(np.random.normal(0, 1, (10 ** 2, 100))))
    labels = np.array([num for _ in range(10) for num in range(10)])
    labels = Variable(torch.cuda.LongTensor(labels))
    gen_imgs = Generator(z, labels)
    # 路径自己改
    save_image(gen_imgs.data, "fake_images1/%d.png" % (e+1), nrow=10, normalize=True)

​ 完成所有的训练后,保持一下权重,方便后期用:

# 保存权重
torch.save(Generator.state_dict(),'./save_weights/G_params.pkl')
torch.save(Discriminator.state_dict(),'./save_weights/D_params.pkl')

完整训练代码

def main():
    # 定义基本参数
    device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') # 设备
    batch_size = 256 # batch大小
    epoch = 400 # 训练批次
    lr = 0.002 # 初始学习率
    img_shape = (1,28,28) # 图像shape
    num_classes = 10 # 类别数目
    in_dim = 100 # 噪声维度
    # 创建模型
    Generator = G(in_dim,img_shape,num_classes)
    Discriminator = D(img_shape,num_classes)
    Generator.to(device)
    Discriminator.to(device)
    # 定义损失函数
    loss = nn.MSELoss()
    # 优化器
    optim_G = optim.Adam(Generator.parameters(),lr=lr,betas=(0.5, 0.999))
    optim_D = optim.Adam(Discriminator.parameters(),lr=lr,betas=(0.5, 0.999))
    # optim_G = optim.SGD(Generator.parameters(),lr=lr,momentum=0.9)
    # optim_D = optim.SGD(Discriminator.parameters(),lr=lr,momentum=0.9)
    # 加载数据
    train_dataset = My_Dataset(train=True)
    train_loader = DataLoader(train_dataset,batch_size=batch_size,shuffle=True,drop_last=True)

    # 开始训练
    for e in range(epoch):
        #学习率调整
        if (e + 1) == 100:
            optim_D.param_groups[0]['lr'] /= 10
            optim_G.param_groups[0]['lr'] /= 10
            print("learning rate change!")

        if (e + 1) == 250:
            optim_D.param_groups[0]['lr'] /= 10
            optim_G.param_groups[0]['lr'] /= 10
            print("learning rate change!")
        for i,(batch_img,batch_label) in enumerate(train_loader):
            # 放入设备中
            batch_img,batch_label = batch_img.to(device),batch_label.to(device)
            # 创建噪声向量和标签
            # z = [batch,100] , labels = [64] 为要生成的数字图片标签
            z = Variable(torch.cuda.FloatTensor(np.random.normal(0, 1, (batch_size, in_dim))))  # 噪声样本
            g_labels = Variable(torch.cuda.LongTensor(np.random.randint(0, num_classes, batch_size)))  # 对应的标签
            #  用于计算损失的标签,一个全为1,一个全为0
            valid = Variable(torch.cuda.FloatTensor(batch_size, 1).fill_(1.0), requires_grad=False)  # torch.Size([64, 1])
            fake = Variable(torch.cuda.FloatTensor(batch_size, 1).fill_(0.0), requires_grad=False)  # torch.Size([64, 1])
            # 生成器根据噪声和标签生成假的图片
            optim_G.zero_grad()
            g_img = Generator(z,g_labels)
            # 判别器判别,输出的为[batch,1],输出的是概率值,越接近1,表示生成的图片越真实
            g_class = Discriminator(g_img,g_labels)
            # 损失
            g_loss = loss(g_class,valid)
            g_loss.backward()
            optim_G.step()
            # 训练判别器
            optim_D.zero_grad()
            # 实图片的损失,目的就是希望其与真实标签1的损失最小
            d_real_class = Discriminator(batch_img,batch_label)
            d_real_loss = loss(d_real_class,valid)
            # g_img.detach() 不要忘记detach,不然会报错的,因为前面backward已经释放了
            # 生成的虚假图片的损失,目的就是希望其与虚假标签0的损失最小
            d_fake_class = Discriminator(g_img.detach(),g_labels)
            d_fake_loss = loss(d_fake_class,fake)
            all_loss = (d_fake_loss+d_real_loss)/2
            all_loss.backward()
            optim_D.step()
            # 每个step打印损失
            print('epoch{%d},batch{%d},D_loss: %.5f,G_loss: %.5f' % (e+1,i+1,all_loss.item(),g_loss.item()))
        # 每一个epoch,看看生成器生成的图片咋样了
        z = Variable(torch.cuda.FloatTensor(np.random.normal(0, 1, (10 ** 2, 100))))
        labels = np.array([num for _ in range(10) for num in range(10)])
        labels = Variable(torch.cuda.LongTensor(labels))
        gen_imgs = Generator(z, labels)
        # 路径自己改
        save_image(gen_imgs.data, "fake_images1/%d.png" % (e+1), nrow=10, normalize=True)
    # 保存权重
    torch.save(Generator.state_dict(),'./save_weights/G_params.pkl')
    torch.save(Discriminator.state_dict(),'./save_weights/D_params.pkl')

6. 训练结果展示与思考:

​ 我尝试了训练400次,运行中间的结果展示如下:

在这里插入图片描述

其实运行的效果,我自己不是很满意。另外,在运行过程中,发现一个非常重要的事情:训练经常容易崩溃,即我运行到50epoch的时候,效果已经不错了,但是会突然损失值增大到一个难以接受的值,导致训练崩溃

​ 关于这一点,应该就是CGAN、GAN的缺点,就是训练不稳定,相比于原始GAN,CGAN的优点是可以控制输出长什么样,但是稳定性方面仍然有所欠缺。我尝试过换一种训练方式,比如先训练好判别器,再训练生成器,试图让优秀学生(判别器)带动差生(生成器),但是效果不佳。

​ 因此,后期我会尝试换一种更稳定的GAN网络,看看是否仍然会遇到同样的问题。

7. 预测:

​ 这个非常简单,由于训练完毕后,我们保存了模型的参数,因此可以直接把生成器参数拿来用。这里的预测,指的是生成一个张图片。

​ 代码如下:

import torch
import numpy as np
from torch.autograd import Variable
from torchvision.utils import save_image

from network_files.Generator import Generator as G

img_shape = (1,32,32)
num_classes = 10
in_dim = 100

# 创建生成器模型
G_model = G(in_dim,img_shape,num_classes)
# 加载参数
G_model.load_state_dict(torch.load('./save_weights/G_params.pkl'))
# 随机生成100个图片并显示
z = Variable(torch.FloatTensor(np.random.normal(0, 1, (10 ** 2, 100))))
labels = np.array([num for _ in range(10) for num in range(10)])
labels = Variable(torch.LongTensor(labels))
gen_imgs = G_model(z, labels)
save_image(gen_imgs.data, "fake_images/%d.png" % (100), nrow=10, normalize=True)

8. 总结:

​ 完成了CGAN,感觉生成对抗的神奇和有趣,进一步了解损失函数控制生成目标的含义。

​ 不过缺点是CGAN生成不稳定且效果并不是很好,后期可以考虑试试其它的GAN网络,看看效果是不是更好点。

参考资料

博客:
https://blog.csdn.net/qq_42721935/article/details/126600071
https://blog.csdn.net/weixin_38052918/article/details/107911739
GitHub项目:
https://github.com/znxlwm/pytorch-MNIST-CelebA-cGAN-cDCGAN

完整代码

链接:https://pan.baidu.com/s/14ZXgMaEuXgM2ETrbHBvOHg 
提取码:izol 

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

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

相关文章

离散数学下 ---格与布尔代数

(一)目录 格的定义及性质 子格 分配格 有补格 布尔代数 (二) 内容 格的定义与性质 补充点: 格与布尔代数,它们与群的基本不同之处是:格与布尔代数的基集都是一个偏序集。 格是一个具…

华硕主板win11设置关机状态下USB接口不通电

华硕主板win11设置关机状态下USB接口不通电 我新装机子在关机之后usb还是通电,导致一些外接设备显示灯常亮着非常不方便。因此我尝试把其关掉。 我的是华硕主板,不同主板可能设置方式不一样,请注意。 请按照如下步骤操作: 计算机…

NetApp FAS2554故障灯常亮case处理过程分享

近期处理了一个NetApp FAS2554前面故障灯一直点亮的case,本文对于分析过程和最终的问题发现做一个分享,欢迎讨论,可以add wechat at StorageExpert。 外观检查: 客户反馈,一套FAS2554存储,没有任何扩展柜…

文献阅读(52)—— Integration self-attention and convolution

文献阅读(52)—— Integration self-attention and convolution 文章目录 文献阅读(52)—— Integration self-attention and convolution先验知识/知识拓展文章结构背景文章方法1. Relating Self-Attention with Convolution 文章…

C++11 函数对象、Lambda表达式 绑定器

1. 函数对象和绑定器 函数对象&#xff1a; 重载了operator()运算符的类的对象。 STL中的原本的绑定器&#xff1a; STL中的绑定器可将二元函数对象绑定为一元函数对象。有如下示例帮助回顾&#xff1a; #include <iostream> #include <vector> #include <…

「 计算机网络 」Cookie、Session、Token、JWT 原理详解

「 计算机网络 」Cookie、Session、Token、JWT 原理详解 参考&鸣谢 傻傻分不清之 Cookie、Session、Token、JWT 详解 Cookie&#xff0c;Session&#xff0c;Token 一文彻底搞懂Cookie、Session、Token到底是什么 文章目录 「 计算机网络 」Cookie、Session、Token、JWT 原…

机器学习 day13(正则化,线性回归的正则化)

正则化的思想 如果特征的参数值更小&#xff0c;那么对模型有影响的特征就越少&#xff0c;模型就越简单&#xff0c;因此就不太容易过拟合 如上图所示&#xff0c;成本函数中有W₃和W₄&#xff0c;且他们的系数很大&#xff0c;要想让该成本函数达到最小值&#xff0c;就得使…

基于PostGIS的曲线拐点计算

在这篇博文中&#xff0c;我将介绍一种查找曲线拐点的方法。 一个简单的理解方式&#xff1a;将曲线想象成我们正在行驶的道路&#xff0c;我们想要找到我们停止右转并开始左转或反之的点&#xff0c;如下所示&#xff1a; 我们将展示解决方案的草图和 PostGIS 中的实际实施。…

AR VR 到底哪种技术可以改变未来?

随着科技的不断进步&#xff0c;虚拟现实&#xff08;VR&#xff09;和增强现实&#xff08;AR&#xff09;技术已经成为了当今科技领域的热门话题。VR和AR的出现&#xff0c;为人们带来了前所未有的体验和感受&#xff0c;也为各行各业的发展提供了新的机遇。但是&#xff0c;…

MySQL学习---14、变量、定义条件和处理程序

1、变量 在MySQL数据库的存储过程和函数中&#xff0c;可以使用变量来存储查询或计算的中间结果数据&#xff0c;或者输出最终的结果数据。 在MySQL数据库中&#xff0c;变量分为&#xff1a;1、系统变量2、用户自定义变量1、系统变量 1.1.1 系统变量分类 1、变量由系统定义…

SpringCloud使用SkyWalking实现分布式链路追踪2以及Prometheus全方位监控告警系统

文章目录 一、SkyWalking链路追踪使用Elasticsearch数据库1、SkyWalking连接Elasticsearch数据库2、SkyWalking自定义链路追踪3、SkyWalking的调用日志 二、SkyWalking链路追踪的告警功能1、SkyWalking的告警规则2、SkyWalking自定义告警规则3、SkyWalking实现网络钩子Webhooks…

基于springboot+vue的校园任务订单配送管理系统

随着我国教育制度的改革的发展&#xff0c;各大高校的学生数量也在不断的增加。当前大学生的生活和消费习惯等导致他们更喜欢通过网络来获取自己想要的商品和服务&#xff0c;这也是导致当前校园配送盛行的主要原因。为了能够让更多的学生享受到校园配送的服务&#xff0c;我们…

记录一次华三交换机配置IP地址和SNMP服务的过程

一、华三交换机配置IP地址 1、将交换机的console口连接到台式机&#xff0c;然后通过XShell连接到COM口 2、进入华三交换机的系统配置界面 命令&#xff1a;system-view 示例&#xff1a;system-view 3、创建vlan。输入vlan vlan-id命令创建一个VLAN。其中&#xff0c;vlan…

FreeRTOS_移植和配置

目录 1. 什么是FreeRTOS&#xff1f; 2. FreeRTOS 特点 3. FreeRTOS 移植 3.1 验证程序 1. 什么是FreeRTOS&#xff1f; 我们先看 FreeRTOS 的名字&#xff0c;可以分成两部分&#xff1a;Free 和 RTOS&#xff0c;Free 就是免费的、自由的、不受约束的意思&#xff0c;RTO…

016-fty_crkme3

运行程序 check错误 查壳 有UPX壳&#xff0c;ESP定律脱壳 载入OD分析 搜索字符串 向上找&#xff0c;找到段首 开始分析算法 搜先判断输入是否为空 再下面开始判断输入的字符除了 “-” 是不是满足0-9 如果满足的话&#xff0c;要再判断长度&#xff0c;0x9 到 0xB&#x…

【计算机组成原理】第一章 计算系统概论

系列文章目录 第一章 计算系统概论 第二章 运算方法和运算器 第三章 多层次的存储器 第四章 指令系统 第五章 中央处理器 第六章 总线系统 第七章 外围设备 文章目录 系列文章目录前言第一章 计算机系统概论1.1 计算机的分类1.2 计算机发展简史1.2.1 计算机的五代变化1.2.2 半…

19 KVM管理虚拟机-虚拟机生命周期管理命令

文章目录 19 KVM管理虚拟机-虚拟机生命周期管理命令19.1 概述19.2 前提条件19.3 命令使用说明 19 KVM管理虚拟机-虚拟机生命周期管理命令 19.1 概述 用户可以使用virsh命令工具管理虚拟机生命周期。本节介绍生命周期相关的命令以指导用户使用。 19.2 前提条件 执行虚拟机生…

uboot symbol value ‘‘ invalid for SYS_TEXT_BASE 解决方法

环境 Win10 64位 ubuntu 20.04 虚拟机 VMware Workstation 16 Pro gcc 交叉编译工具链&#xff1a; ARM 官方 gcc version 11.2.1 20220111 NUC980 uboot 版本 &#xff1a;尝试移植到 u-boot-2019.10&#xff0c;官方当前 u-boot 版本为 2016.11 问题描述 升级 u-boot …

ThinkPHP6 模板引擎普通标签中,模板引擎运算符函数,循环标签,判断标签的使用,及一些特殊标签

ThinkPHP6 模板引擎普通标签中&#xff0c;模板引擎运算符函数&#xff0c;循环标签&#xff0c;判断标签的使用&#xff0c;及一些特殊标签 模板引擎支持普通标签和XML标签方式两种标签定义&#xff0c;分别用于不同的目的&#xff1a; 标签类型描述普通标签主要用于输出变量…

Linux 安装 JDK、MySQL、Tomcat(图文并茂)

所需资料 下载 1.1 软件安装方式 在Linux系统中&#xff0c;安装软件的方式主要有四种&#xff0c;这四种安装方式的特点如下&#xff1a; 安装方式特点二进制发布包安装软件已经针对具体平台编译打包发布&#xff0c;只要解压&#xff0c;修改配置即可rpm安装软件已经按照re…