PyTorch实战3:天气识别

news2024/11/15 4:27:47
  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍦 参考文章:365天深度学习训练营-第P3周:天气识别
  • 🍖 原作者:K同学啊|接辅导、项目定制

目录

    • 一、前期准备
        • 1、导入数据
        • 2、transforms.Compose详解
        • 3、图像处理(数据增强)
        • 4、加载数据集
        • 5、划分数据集
    • 二、构建简单的CNN网络
        • 1、torch.nn.Flatten()详解
        • 2、x.view()详解
        • 3、两者之间的区别
        • 4、案例结果展示
    • 三、指定图片进行预测
        • 1、⭐torch.squeeze()详解
        • 2、⭐torch.unsqueeze() 详解
        • 3、某张图片预测
    • 四、总结

本文案例学习主要有两点:

  • 本地读取并加载数据
  • 调用模型识别一张本地图片

一、前期准备

1、导入数据

读取指定目录下的所有图像文件,将它们的路径存储在一个列表中,并提取每个图像所属的类别名称。

首先,导入了四个Python库:os、PIL、random和pathlib。其中,os库提供了访问操作系统功能的接口;PIL库是Python图像处理库;random库提供了生成随机数的函数;pathlib库提供了以面向对象的方式操作文件路径的方法。

然后,定义了一个字符串变量"data_dir",它指定了包含图像数据集的目录。接着,使用pathlib.Path()方法将"data_dir"转换为Path对象类型,以便可以调用该对象上的方法执行一些操作。

接下来,使用data_dir.glob(‘*’)方法获取"data_dir"目录下的所有文件路径,并将这些路径存储到data_paths列表中。

在下一行代码中,使用列表推导式和split()方法从每个文件的路径中获取类别名称,并将这些名称存储到classeNames列表中。

最后,打印classeNames列表,即可查看所有类别的名称。

代码如下:

import os,PIL,random,pathlib

data_dir = './weather_photos/'
data_dir = pathlib.Path(data_dir)

data_paths = list(data_dir.glob('*'))
classeNames = [str(path).split("\\")[1] for path in data_paths]
classeNames

2、transforms.Compose详解

transforms.Compose是PyTorch中一个用于数据预处理的类,它允许用户将多个数据预处理操作组合在一起。具体而言,transforms.Compose会接受多个transform函数作为输入,并返回一个新的transform函数,该函数将按照传入的顺序依次执行每个transform函数。

例如,假设我们想要对一张图片进行数据增强操作,包括随机裁剪、水平翻转和归一化。我们可以使用以下代码:

import torchvision.transforms as transforms

# 定义数据增强操作
transform = transforms.Compose([
    transforms.RandomCrop(32, padding=4),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

# 对图片应用数据增强操作
img_augmented = transform(img)

在上面的示例中,我们首先定义了一个transform对象,其中包含四个数据预处理操作:随机裁剪、水平翻转、将图像转换为张量以及归一化。然后,我们将这个transform对象应用到一张图片上,得到了经过数据增强处理后的新图片。

需要注意的是,transforms.Compose只能用于序列化的数据预处理操作,也就是说每个操作必须能够被序列化为一个字符串,并且可以通过反序列化得到一个可用的操作函数。因此,如果你需要定义自己的数据预处理操作,并将其添加到transforms.Compose中,则必须确保这些操作是可序列化的。

3、图像处理(数据增强)

接下来编写用来进行图像处理的代码。

下面是每行代码的解释:

total_datadir = './weather_photos/'

设置数据集所在的目录路径。

train_transforms = transforms.Compose([
    transforms.Resize([224, 224]),  # 将输入图片resize成统一尺寸
    transforms.ToTensor(),          # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    transforms.Normalize(           # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
        mean=[0.485, 0.456, 0.406], 
        std=[0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
])

定义数据处理的步骤,这里采用了三个步骤:

  • 将图像 resize 到固定大小(224x224),保证输入模型的图片尺寸一致。
  • 将 PIL Image 或 numpy.ndarray 转换为 tensor,并归一化到 [0,1] 之间。
  • 进行标准化处理,将输入的图片转换为标准正太分布(高斯分布),使得模型更容易收敛。

其中,meanstd 是从数据集中随机抽样计算得到的均值和标准差。

4、加载数据集

加载数据集的代码如下:

total_data = datasets.ImageFolder(total_datadir,transform=train_transforms)

使用 PyTorch 提供的 ImageFolder 类加载数据集,将数据集所在路径和上面定义好的数据处理方式传入。该类会自动将数据集按照文件名所在的文件夹分类,并将分类信息存储在 classes 属性中。此外,还有一个 class_to_idx 属性,保存了每个分类对应的索引,方便后续训练模型时使用。最终,该代码段返回处理好的数据集对象 total_data

5、划分数据集

使用PyTorch库中的torch.utils.data.random_split函数,将给定的数据集total_data按照指定比例(80%和20%)随机划分为训练集和测试集。

具体来说,首先计算出训练集大小train_size,即将总数据集大小乘以0.8,然后计算出测试集大小test_size,即总数据集大小减去训练集大小。接着,调用random_split函数,将总数据集和一个包含两个元素的列表作为参数传入,其中第一个元素是训练集的大小,第二个元素是测试集的大小。该函数会返回两个新的数据集对象,分别对应划分好的训练集和测试集。

代码如下:

# 计算训练集大小和测试集大小
train_size = int(0.8 * len(total_data))
test_size  = len(total_data) - train_size

# 调用random_split函数进行随机划分
# total_data: 需要划分的原始数据集
# [train_size, test_size]: 包含两个元素的列表,分别指定训练集和测试集的大小
train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])

# 打印划分好的训练集和测试集
train_dataset, test_dataset

使用 PyTorch 的 DataLoader 对象来创建训练集和测试集的数据加载器,以便在训练和测试期间对数据进行批处理。

batch_size = 32  # 每次迭代使用的样本数

# 创建训练集和测试集的数据加载器
train_dl = torch.utils.data.DataLoader(train_dataset,
                                           batch_size=batch_size,
                                           shuffle=True,  # 在每个 epoch 开始之前打乱数据顺序
                                           num_workers=1)  # 使用一个工作线程加载数据

test_dl = torch.utils.data.DataLoader(test_dataset,
                                          batch_size=batch_size,
                                          shuffle=True,
                                          num_workers=1)

train_datasettest_dataset 应该是一个 PyTorch 数据集对象,包含了训练集和测试集的样本、标签等信息。shuffle 参数指定是否在每个 epoch 开始时打乱数据,num_workers 参数指定用于加载数据的工作线程数量。

这里我们创建了两个 DataLoader 对象:train_dltest_dl,分别用于加载训练集和测试集数据,并将其分为大小为 batch_size 的小批次(batch)。在训练过程中,可以使用这些小批次来计算损失函数并更新模型参数,从而逐步提高模型性能。

二、构建简单的CNN网络

卷积层、全连接层、池化层以及批量归一化层的详解可移步至PyTorch实战2:彩色图片识别(CIFAR10)

在此详细介绍torch.nn.Flatten()x.view()

1、torch.nn.Flatten()详解

torch.nn.Flatten() 是 PyTorch 中的一个层(Layer),它可以将多维张量(tensor)展平为一维。具体来说,该层可以将大小为 (batch_size, C, H, W) 的张量展平为大小为 (batch_size, C*H*W) 的张量。

下面是一个简单的例子:

import torch
import torch.nn as nn

x = torch.randn(2, 3, 4, 5) # 生成一个大小为 (2, 3, 4, 5) 的随机张量
flatten = nn.Flatten()
y = flatten(x)

print(f"x: {x.size()}") # 输出 x 的大小
print(f"y: {y.size()}") # 输出 y 的大小

输出结果如下:

x: torch.Size([2, 3, 4, 5])
y: torch.Size([2, 60])

从输出结果可以看出,x 的大小为 (2, 3, 4, 5),而 y 的大小为 (2, 60),即将 x 展平为一个大小为 (2, 60) 的张量。

2、x.view()详解

x.view() 是 PyTorch 中的一个函数,它可以将一个张量 x 重塑(reshape)成另外一个形状。与 torch.flatten() 不同,x.view() 可以将张量变换为任何形状,只要元素总数保持不变即可。

具体来说,如果 x 的形状为 (n1, n2, ..., nk),那么通过 x.view(a1, a2, ..., ak),可以将 x 变换为形状为 (a1, a2, ..., ak) 的新张量。需要注意的是,x 和变换后的新张量共享存储空间,因此在修改其中一个张量的值时,另一个张量的值也会相应地发生改变。

下面是一个简单的例子:

import torch

x = torch.arange(24).view(2, 3, 4) # 生成一个大小为 (2, 3, 4) 的张量
y = x.view(6, 4) # 将 x 重塑为一个大小为 (6, 4) 的新张量

print(f"x: {x}")
print(f"y: {y}")

x[0, 1, 2] = -1 # 修改 x 中的一个元素

print(f"x: {x}")
print(f"y: {y}")

输出结果如下:

x: tensor([[[ 0,  1,  2,  3],
         [ 4,  5,  6,  7],
         [ 8,  9, 10, 11]],

        [[12, 13, 14, 15],
         [16, 17, 18, 19],
         [20, 21, 22, 23]]])
y: tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]])
x: tensor([[[ 0,  1,  2,  3],
         [ 4,  5, -1,  7],
         [ 8,  9, 10, 11]],

        [[12, 13, 14, 15],
         [16, 17, 18, 19],
         [20, 21, 22, 23]]])
y: tensor([[ 0,  1,  2,  3],
        [ 4,  5, -1,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]])

从输出结果可以看出,首先定义了一个大小为 (2, 3, 4) 的张量 x,然后通过 x.view(6, 4) 将其重塑为大小为 (6, 4) 的张量 y。接着,修改了 x 中的一个元素,可以看到 y 中相应位置的值也发生了改变,这是因为 xy 共享存储空间。

3、两者之间的区别

torch.nn.Flatten()x.view() 都可以用来将 tensor 扁平化,但是它们的使用方式和具体实现有所不同。

torch.nn.Flatten() 是一个模块,当在模型中调用时,它会自动将输入 tensor 扁平化。例如:

import torch.nn as nn
flatten = nn.Flatten()

x = torch.randn(3, 4, 5)
y = flatten(x)
print(y.shape)  # 输出 torch.Size([60])

x.view() 是一个 tensor 的方法,可以用来将 tensor 重塑为指定的形状。例如:

x = torch.randn(3, 4, 5)
y = x.view(3, 20)
print(y.shape)  # 输出 torch.Size([3, 20])

虽然这两个函数都能够扁平化 tensor,但是它们的语义和用法有所不同。torch.nn.Flatten() 更适合在神经网络模型的构建中使用,而 x.view() 更适合在计算过程中手动调整 tensor 形状。

两者仅仅是一种数据集拉伸操作(将二维数据拉伸为一维),torch.flatten()方法不会改变x本身,而是返回一个新的张量。而x.view()方法则是直接在原有数据上进行操作。

  • 具体网络结构在此不做详细赘述,下一篇基于水质图像识别的水质评估会进行详细的描述
  • 另:模型训练与训练结果的可视化请移步至PyTorch实战1:实现mnist手写数字识别学习

4、案例结果展示

  • 训练过程:

在这里插入图片描述

  • 结果可视化

在这里插入图片描述

三、指定图片进行预测

1、⭐torch.squeeze()详解

torch.squeeze()是PyTorch中的一个函数,它用于移除张量中大小为1的维度。具体来说,如果张量在某个维度上只有一个元素,那么这个维度可以被“挤压”或者“挤掉”,从而生成一个新的形状更小的张量。

  • 对数据的维度进行压缩,去掉维数为1的的维度

函数原型:

torch.squeeze(input, dim=None, *, out=None) 

函数的参数包括:

  • input (Tensor):需要挤压的输入张量。
  • dim (int, optional):指定需要挤压的维度。如果不指定,将会挤压所有大小为1的维度。

函数返回一个新的张量。

以下是一些示例代码:

import torch

# 示例1
x = torch.randn(2, 1, 3, 1, 4)
y = torch.squeeze(x)
print(y.shape) # 输出torch.Size([2, 3, 4])

# 示例2
x = torch.randn(2, 1, 3, 1, 4)
y = torch.squeeze(x, dim=1)
print(y.shape) # 输出torch.Size([2, 3, 1, 4])

在第一个示例中,输入张量x的形状为(2, 1, 3, 1, 4),其中有两个大小为1的维度,分别位于第二和第四维。使用torch.squeeze(x),将移除这两个维度,输出的新张量y的形状为(2, 3, 4)

在第二个示例中,指定了需要挤压的维度为1,因此只移除了第二个维度上的大小为1的元素,并保留了其他维度。输出的新张量y的形状为(2, 3, 1, 4),仍然有一个大小为1的维度。

2、⭐torch.unsqueeze() 详解

torch.unsqueeze() 是 PyTorch 中用于增加张量维度的函数,可以将给定张量的维度增加一维。具体来说,它可以在指定位置插入一个大小为 1 的新维度。

  • 对数据维度进行扩充。给指定位置加上维数为一的维度

函数定义如下:

torch.unsqueeze(input, dim)

参数说明:

  • input (Tensor):输入张量。
  • dim (int):插入新维度的位置。该参数是可选的,默认为零,即在第一维插入新维度。

返回值:

  • 返回插入新维度后的新张量。

例如,对于一个形状为 (3, 4) 的二维张量 x,我们可以通过以下方式在第一维插入新维度:

import torch

x = torch.randn(3, 4)
print(x.shape) # 输出 (3, 4)

y = torch.unsqueeze(x, 0)
print(y.shape) # 输出 (1, 3, 4)

上述代码中,使用 torch.randn() 创建了一个形状为 (3, 4) 的二维张量 x。然后,使用 torch.unsqueeze(x, 0) 在第一维插入新维度,得到一个形状为 (1, 3, 4) 的三维张量 y。其中,第一维的大小为 1。

可以看到,torch.unsqueeze() 可以方便地增加张量的维度,这在神经网络中非常有用。例如,在图像识别任务中,通常需要将二维图像张量增加一个通道维度,以便于输入卷积层。

3、某张图片预测

代码流程:

  1. 使用PIL库加载待预测图片。
  2. 将图片按照指定的预处理方法transform转换成模型输入所需的Tensor格式。
  3. 在图片的第0维上添加一个新的维度,以满足模型输入为4维(batch_size, channels, height, width)的要求。
  4. 将图片Tensor输入到已训练的分类器模型中,获取模型输出。
  5. 从模型输出中找到最大值及其对应的索引,即为预测结果所属的类别。
  6. 根据类别索引在classes列表中查找对应的类别名称。
  7. 返回预测结果。

下面是代码解释:

from PIL import Image  # 导入PIL库,用于处理图像

# 获取数据集中的类别列表
classes = list(total_data.class_to_idx)

def predict_one_image(image_path, model, transform, classes):
    '''
    对单张图片进行分类预测
    
    参数:
    image_path:待预测图片路径
    model:已训练模型
    transform:数据预处理函数
    classes:数据集类别列表
    
    返回值:
    无
    '''
    
    # 打开待预测的图片,并转换为RGB格式
    test_img = Image.open(image_path).convert('RGB')
    # plt.imshow(test_img)  # 展示预测的图片

    # 对图片进行数据预处理
    test_img = transform(test_img)
    img = test_img.to(device).unsqueeze(0)  # 将图片加入一个batch中,这里的batch size为1
    
    model.eval()  # 设置模型为评估模式
    output = model(img)  # 对图片进行分类预测

    _,pred = torch.max(output,1)  # 获取预测结果中概率最高的类别标签
    pred_class = classes[pred]  # 根据类别标签获取类别名称
    print(f'预测结果是:{pred_class}')  # 输出预测结果

需要注意的是,该代码中需要事先定义好模型的结构和参数,并将其加载到 GPU 上进行训练。同时也需要一个数据预处理函数(transform),用于将输入的图片转换为 PyTorch 可以处理的格式。

参数说明:

  • image_path: 待预测图片路径。
  • model: 训练好的分类器模型。
  • transform: 数据预处理方法,用于将图片转换成模型输入所需格式。
  • classes: 类别名称列表,记录模型预测结果对应的类别名称。

调用函数代码如下:

predict_one_image(image_path='./4-data/Monkeypox/M01_01_00.jpg',
                  model=model, 
                  transform=train_transforms, 
                  classes=classes)

四、总结

本次学习主要涉及了PyTorch中CNN网络的搭建、数据处理和模型预测。在准备阶段,通过导入数据、使用transforms.Compose进行数据增强、加载数据集以及划分数据集等操作为构建CNN网络做好准备。

在构建CNN网络的过程中,介绍了torch.nn.Flatten()和x.view()两个函数,并讲解了它们之间的区别。最后,还学习了如何使用指定的图片进行预测,包括torch.squeeze()和torch.unsqueeze()的详解和某张图片的预测方法。

通过本次学习,可以更好地掌握PyTorch中CNN网络的构建和使用,从而提高自己的深度学习水平。

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

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

相关文章

JavaWeb+JSP内置对象+Session+Cookie+ 过滤器Filter+ 监听器Listener(超详细)

🙈作者简介:练习时长两年半的Java up主 🙉个人主页:老茶icon 🙊 ps:点赞👍是免费的,却可以让写博客的作者开兴好久好久😎 📚系列专栏:Java全栈,计…

从 Milvus 2.2 到 2.2.6,我们是如何持续稳定升级的

最近,Milvus 发布了 2.2.6 版本,在修复了一些 bug 后,Milvus 变得越发稳定。 事实上,自 Milvus 升级至 2.X 版本以来,我们一直在努力改进及优化,推出了诸如从文件中批量导入数据、基于磁盘的近似最近邻&…

通过ppt制作圆形图标及自定义形状图形制作

今天做PPT时,需要一个圆形的截图,日常截图都是方形的,能不能把截图裁剪为一个圆形呢?于时从网上找了一下,发现还真有。 制作工具:PPT 制作步骤: 1、准备图片 将截图或图片插入PPT中 2、在…

(04)基础强化:接口,类型转换cast/convert,异常处理,传参params/ref/out,判断同一对象

一、复习 1、New的截断是指什么? new除了新开空间创建初始化对象外,还有一个隐藏父类同名方法的作用。 当子类想要隐藏父类同名的方法时用new,用了new后父类同名方法将到此为止,后面 继承的…

electron+vue3全家桶+vite项目搭建【15】vue3+sass实现多主题一键切换,支持electron多窗口同步更新

文章目录 引入实现效果展示实现思路整理实现步骤1.定义全局主题样式变量2.定义主题模板3.封装颜色工具类4.初始化主题色5.主进程监听颜色修改6.补充主题状态管理7.主题一键切换组件8.测试案例 引入 我们之前在这篇文章中集成了 sass,接下来我们结合sass的变量定义&…

银行数字化转型导师坚鹏:宏观经济形势分析与银行发展模式创新

宏观经济形势分析与银行发展模式创新 课程背景: 很多学员存在以下问题: 不知道我国目前的宏观经济形势? 不清楚宏观环境对我国经济的影响? 不知道银行未来主要的发展模式? 课程特色: 精彩解读宏…

最新:机器学习在生态、环境经济学中的实践技术应用及论文写作

查看原文>>>最新:机器学习在生态、环境经济学中的实践技术应用及论文写作 目录 专题一、理论基础与软件介绍 专题二、数据的获取与整理 专题三、常用评价方法与相关软件详细教学(案例详解) 专题四、写作要点与案例的讲解 近年来…

Redis数据库常用语句

Redis数据库常用语句 前言1. 键(Key)的基本操作1.1 增加新的键值对1.2 访问键的值1.3 修改键值对1.4 键值对的删除1.5 判断键值对是否存在1.6 获取所有键1.7 删除所有的键: 2. Redis 中的列表2.1 列表加入新元素2.2 获取列表长度2.3 获取指定下标的元素2.4 获取指定…

Android App 架构 面试专题,你可能会被问到的 20 个问题

iveData 是否已经被弃用? 没有被弃用。在可以预见的未来也没有废弃的计划。 LiveData 可以使用简单的方式获取一个易于观察、状态安全的对象。虽然其缺少一些丰富的操作符,但是对于一些简单的 UI 业务场景已经足够。 Flow 有 LiveData 相同的功能,其…

1.栈的介绍-C语言调用函数(二)

1.栈的介绍-C语言调用函数(一)_双层小牛堡的博客-CSDN博客 接着上面 函数调用的约定 在栈帧中 主要的是主调函数如何存入实参 让被调用函数能够访问 这种是通过函数见的调用规定来规范的 并且 调用规定还规范了 函数执行完后应该由主函数实现 清除参…

[测试猿课堂]小白怎么学测试?史上最全《软件测试》学习路线

熬夜3天,联合3位猿计划教育的总监级授课老师,整理了这份《软件测试小白学习路线》,全文接近6000字,请大家耐心看完! 对于很多想通过自学转行软件测试的同学,痛点并不是学习动力,而是找不到清晰…

Apache SeaTunnel 3 分钟入门指南

简介 新一代分布式超高性能云原生数据同步工具 - Apache SeaTunnel 已经在B站、腾讯云、字节等数百家公司使用。 SeaTunnel 是 Apache 软件基金会下的一个高性能开源大数据集成工具,为数据集成场景提供灵活易用、易扩展并支持千亿级数据集成的解决方案。SeaTunnel …

《计算机网络--自顶向下方法》第三章--运输层

3.1概述和运输层服务 运输层协议为运行再不同主机上的应用进程之间提供了逻辑通信(logic communication)功能 运输层协议是在端系统中而不是在路由器中实现的 3.1.1运输层和网络层的关系 运输层协议至工作在端系统中 在端系统中,运输层…

基于Mybatis使用MySql存储过程,实现数据统计功能

1、前言 作为一个工作了很多年的程序员来说,没有在实际工作中真正使用过存储过程,其实对存储过程本身有过了解和学习,在日常的学习中,也会看过一些存储过程的相关介绍,不过“纸上得来终是浅”,正好这次做统…

Linux 利用网络同步时间

yum -y install ntp ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ntpdate ntp1.aliyun.com 创建加入crontab echo "*/20 * * * * /usr/sbin/ntpdate -u ntp.api.bz >/dev/null &" >> /var/spool/cron/rootntp常用服务器 中国国家授…

力扣sql中等篇练习(十三)

力扣sql中等篇练习(十三) 1 每位学生的最高成绩 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 #先找到最大的元素 然后分组即可,不用管某些字段(grade)是不是聚合字段 SELECT e1.student_id,min(e1.course_id) course_id,e1.grade FROM Enrollment…

setup.py方式打包自己的python代码并可以用pip install安装

setup.py方式打包自己的python代码并可以用pip install安装 所需文件及目录规范示例演示引用自己打的包 所需文件及目录规范 注意setup.py文件和MANIFEST.in文件需要放在和你需要打包的目录同一级下,例如我这里需要打包的就是webconsole文件夹(这里webc…

gl-opendrive插件(车俩3D仿真模拟自动驾驶)

简介 本插件基于免费opendrive开源插件、Threejs和Webgl三维技术、vue前端框架,blender开源建模工具等进行二次开发。该插件由本人独立开发以及负责,目前处于demo阶段,功能还需待完善,由于开发仓促代码还需优化。 因此&#xff…

35岁测试人,面临职场危机,打了一场漂亮的翻身仗...

“夜深知雪重,时闻折竹声”。雪折,一种在雪的载荷下,植物(多指树)的躯干或枝条被不断堆积的雪花压断的现象。我的刚刚经历了人生的第一次“雪折”。 我是一个有点聪明且勤奋好学的人,从考入省重点大学起&a…

Windows环境下C++ 安装OpenSSL库 源码编译及使用(VS2019)

参考文章https://blog.csdn.net/xray2/article/details/120497146 之所以多次一举自己写多一篇文章,主要是因为原文内容还是不够详细。而且我安装的时候碰到额外的问题。 1.首先确认一下自己的代码是Win32的还是Win64的,我操作系统是64的,忘…