Pytorch 基础

news2024/11/15 15:33:38

CSDN

✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。
🍎个人主页:小嗷犬的个人主页
🍊个人网站:小嗷犬的技术小站
🥭个人信条:为天地立心,为生民立命,为往圣继绝学,为万世开太平。


本文目录

  • Pytorch 简介
    • 什么是 Pytorch
    • 为什么使用 PyTorch
  • PyTorch 基础
    • Tensor 张量
      • 导入 PyTorch
      • 创建一个没有初始化的 5×3 矩阵
      • 创建一个随机初始化的矩阵
      • 创建一个全0,全1,或者对角线为1的矩阵,且数据类型为 long
      • 从数据中直接创建 Tensor
      • 创建一个和已有 Tensor 一样大小的 Tensor
      • 获取 Tensor 的形状
      • 转化为 numpy 数组
      • 绝对值
      • 加法
      • 限定范围
      • 改变形状
      • 获取单元素 Tensor 中的值
      • 使用GPU
    • Variable 变量
      • Autograd 自动求导
        • 创建一个张量并设置 requires_grad = True 用来追踪其计算历史
        • 对这个张量做一次运算
      • 梯度
        • 雅可比矩阵
      • Variable
        • 搭建一个简单的神经网络
        • 使用 Variable 搭建一个自动计算梯度的神经网络
        • 使用 nn.Module 自定义传播函数来搭建神经网络
    • Dataset 数据集
    • nn.Module 模组
      • 使用 torch.nn 内的序列容器 Sequential
      • 使用 nn.Module 定义一个神经网络
    • torch.optim 优化
    • 模型的保存和加载


Pytorch 简介

什么是 Pytorch

Python优先的深度学习框架,支持GPU加速和动态神经网络。

为什么使用 PyTorch

  • 动态图机制,更加灵活
  • 与Python紧密结合,更加方便
  • 支持GPU加速,更加快速
  • 支持分布式训练,更加强大

PyTorch 基础

Tensor 张量

Tensor 相当于多维的矩阵。

Tensor 的数据类型有:(32位浮点型)torch.FloatTensor,(64位浮点型)torch.DoubleTensor,(16位整型)torch.ShortTensor,(32位整型)torch.IntTensor,(64位整型)torch.LongTensor

导入 PyTorch

import torch

创建一个没有初始化的 5×3 矩阵

x = torch.empty(5, 3)
print(x)

创建一个随机初始化的矩阵

#均匀分布[0,1],rand
x = torch.rand(5, 3)
print(x)

#正态分布,randn
x = torch.randn(5, 3)
print(x)

创建一个全0,全1,或者对角线为1的矩阵,且数据类型为 long

#全0
x = torch.zeros(5, 3, dtype=torch.long)
print(x)

#全1
x = torch.ones(5, 3, dtype=torch.long)
print(x)

#对角线为1
x = torch.eye(5, 3, dtype=torch.long)
print(x)

从数据中直接创建 Tensor

#从列表中创建
x = torch.tensor([5.5, 3])
print(x)

#从numpy数组中创建
import numpy as np
x = torch.from_numpy(np.ones(5))
print(x)

创建一个和已有 Tensor 一样大小的 Tensor

x = torch.randn_like(x)
print(x)

获取 Tensor 的形状

print(x.size())

torch.Size 本质上是一个元组,所以支持元组的各种操作。

转化为 numpy 数组

x = x.numpy()
print(x)

绝对值

x = torch.abs(x)
print(x)

加法

y = torch.rand(5, 3)
# 形式一
print(x + y)

# 形式二
print(torch.add(x, y))

# 形式三
result = torch.empty(5, 3)
torch.add(x, y, out=result)
print(result)

# 形式四
y.add_(x)
print(y)

add_ 是原地操作,即改变了 y 的值。所有的inplace操作都有一个 _ 后缀,表示原地操作。
其他运算:sub 减法,mul 乘法,div 除法,pow 幂运算,mm 矩阵乘法,mv 矩阵向量乘法。

限定范围

x = torch.clamp(x, min=0, max=1)
print(x)

改变形状

x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8)  # -1 表示自适应
print(x.size(), y.size(), z.size())

获取单元素 Tensor 中的值

x = torch.randn(1)
print(x.item())

使用GPU

# 判断是否有GPU
print(torch.cuda.is_available())

# 创建一个 Tensor 并放到 GPU 上
x = torch.randn(5, 3)
x = x.cuda()
print(x)

Variable 变量

Autograd 自动求导

创建一个张量并设置 requires_grad = True 用来追踪其计算历史

x = torch.ones(2, 2, requires_grad=True)
print(x)

对这个张量做一次运算

y = x + 2
print(y)
# y 是计算结果,所以它有 grad_fn 属性
print(y.grad_fn)

# 对 y 做更多操作
z = y * y * 3
out = z.mean()
print(z, out)

.requires_grad_() 会改变现有张量的 requires_grad 标志。如果没有指定的话,默认输入的这个标志是 False

a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)

a.requires_grad_(True)
print(a.requires_grad)

b = (a * a).sum()
print(b.grad_fn)

梯度

x = torch.ones(2,2,requires_grad=True)
y = x + 2
z = y * y * 3
out = z.mean()

# 现在开始反向传播,因为 out 是一个标量,则 out.backward() 和 out.backward(torch.tensor(1.)) 等价。
out.backward()

# 打印梯度 d(out)/dx
print(x.grad)

o u t = 1 4 ∑ i z i out=\frac{1}{4}\sum_iz_i out=41izi
z i = 3 ( x i + 2 ) 2 z_i=3(x_i+2)^2 zi=3(xi+2)2
因为 z i ∣ x i = 1 = 27 z_i\bigr\rvert_{x_i=1}=27 zi xi=1=27,所以
∂ o u t ∂ x i = 3 2 ( x i + 2 ) \frac{\partial_{out}}{\partial_{x_i}}=\frac{3}{2}(x_i+2) xiout=23(xi+2)

所以
∂ o u t ∂ x i ∣ x i = 1 = 9 2 = 4.5 \frac{\partial_{out}}{\partial_{x_i}}\bigr\rvert_{x_i=1}=\frac{9}{2}=4.5 xiout xi=1=29=4.5

雅可比矩阵

数学上,若有向量值函数 y = f(x),那么 y 相对于 x 的梯度是一个雅可比矩阵。

J = [ ∂ y 1 ∂ x 1 ⋯ ∂ y 1 ∂ x n ⋮ ⋱ ⋮ ∂ y m ∂ x 1 ⋯ ∂ y m ∂ x n ] J=\begin{bmatrix}\frac{\partial y_1}{\partial x_1} & \cdots & \frac{\partial y_1}{\partial x_n}\\\vdots & \ddots & \vdots\\\frac{\partial y_m}{\partial x_1} & \cdots & \frac{\partial y_m}{\partial x_n}\end{bmatrix} J= x1y1x1ymxny1xnym

通常来说,torch.autograd 是计算雅可比向量积的一个引擎。也就是说,给定任意向量 v,计算乘积$ J·v $。如果 v 恰好是标量函数 l = g(y) 的梯度,也即 v = ( ∂ l ∂ y 1 , ⋯   , ∂ l ∂ y m ) T v=(\frac{\partial l}{\partial y_1}, \cdots ,\frac{\partial l}{\partial y_m})^T v=(y1l,,yml)T,那么根据链式法则,雅可比向量积的计算刚好就是 lx 的导数:

J ⋅ v = [ ∂ y 1 ∂ x 1 ⋯ ∂ y 1 ∂ x n ⋮ ⋱ ⋮ ∂ y m ∂ x 1 ⋯ ∂ y m ∂ x n ] [ ∂ l ∂ y 1 ⋮ ∂ l ∂ y m ] = [ ∂ l ∂ x 1 ⋮ ∂ l ∂ x n ] J·v=\begin{bmatrix}\frac{\partial y_1}{\partial x_1} & \cdots & \frac{\partial y_1}{\partial x_n}\\\vdots & \ddots & \vdots\\\frac{\partial y_m}{\partial x_1} & \cdots & \frac{\partial y_m}{\partial x_n}\end{bmatrix}\begin{bmatrix}\frac{\partial l}{\partial y_1}\\\vdots\\\frac{\partial l}{\partial y_m}\end{bmatrix}=\begin{bmatrix}\frac{\partial l}{\partial x_1}\\\vdots\\\frac{\partial l}{\partial x_n}\end{bmatrix} Jv= x1y1x1ymxny1xnym y1lyml = x1lxnl

雅可比向量积的这一特性使得将外部梯度输入到具有非标量输出的模型中变得非常方便。

x = torch.randn(3, requires_grad=True)
y = x * 2
while y.data.norm() < 1000:
    y = y * 2
print(y)

下面这种情况,y 不再是标量。torch.autograd 不能直接计算完整的雅可比矩阵,但是如果我们只想要雅可比向量积,只需将这个向量作为参数传给 backward

v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)
print(x.grad)

也可以通过将代码块包装在 with torch.no_grad(): 中,来阻止 autograd 跟踪设置了 .requires_grad=True 的张量的历史记录。

print(x.requires_grad)
print((x ** 2).requires_grad)

with torch.no_grad():
    print((x ** 2).requires_grad)

Variable

VariableTensor 的区别是 Variable 会被放入计算图中,然后进行前向传播,反向传播,自动求导。

Variable 是在 torch.autograd.Variable 中的,使用Variable 需要导入 torch.autograd.Variable

from torch.autograd import Variable

x = Variable(torch.Tensor([1]),requires_grad=True)
w = Variable(torch.Tensor([2]),requires_grad=True)
b = Variable(torch.Tensor([3]),requires_grad=True)

y = w * x + b

y.backward()

print(x.grad)
print(w.grad)
print(b.grad)

搭建一个简单的神经网络

import torch

batch_n = 100  # 一个批次中输入数据的数量
hidden_layer = 100  # 经过隐藏层后保留的数据特征的个数
input_data = 1000  # 每个数据包含的数据量
output_data = 10  # 每个输出的数据包含的数据量

x = torch.randn(batch_n, input_data)  # 100*1000
y = torch.randn(batch_n, output_data)  # 100*10

w1 = torch.randn(input_data, hidden_layer)  # 1000*100
w2 = torch.randn(hidden_layer, output_data)  # 100*10

epoch_n = 20  # 训练的次数
learning_rate = 1e-6  # 学习率

for epoch in range(epoch_n):
    h1 = x.mm(w1)  # 100*100
    h1 = h1.clamp(min=0)  # if x < 0 ,x=0
    y_pred = h1.mm(w2)  # 100*10,前向传播预测结果

    loss = (y_pred - y).pow(2).sum()  # 损失函数,即均方误差
    print("Epoch:{}, Loss:{:.4f}".format(epoch, loss))
    grad_y_pred = 2*(y_pred-y)  # dloss/dy
    grad_w2 = h1.t().mm(grad_y_pred)  # dloss/dy * dy/dw2

    grad_h = grad_y_pred.clone()  # 复制
    grad_h = grad_h.mm(w2.t())  # dloss/dy * dy/dh1
    grad_h.clamp_(min=0)  # if x < 0, x = 0
    grad_w1 = x.t().mm(grad_h)

    w1 -= learning_rate*grad_w1  # 梯度下降
    w2 -= learning_rate*grad_w2

使用 Variable 搭建一个自动计算梯度的神经网络

import torch
from torch.autograd import Variable

batch_n = 100  # 一个批次中输入数据的数量
hidden_layer = 100  # 经过隐藏层后保留的数据特征的个数
input_data = 1000  # 每个数据包含的数据量
output_data = 10  # 每个输出的数据包含的数据量

x = Variable(torch.randn(batch_n, input_data),
             requires_grad=False)  # requires_grad = False不保留梯度
y = Variable(torch.randn(batch_n, output_data), requires_grad=False)
w1 = Variable(torch.randn(input_data, hidden_layer),
              requires_grad=True)  # requires_grad = True自动保留梯度
w2 = Variable(torch.randn(hidden_layer, output_data), requires_grad=True)

epoch_n = 20
learning_rate = 1e-6

for epoch in range(epoch_n):
    y_pred = x.mm(w1).clamp(min=0).mm(w2)  # y_pred = w2 * (w1 * x)
    loss = (y_pred-y).pow(2).sum()  # 损失函数
    print("Epoch:{},Loss:{:.4f}".format(epoch, loss))

    loss.backward()  # 后向传播计算

    w1.data -= learning_rate*w1.grad.data
    w2.data -= learning_rate*w2.grad.data

    w1.grad.data.zero_()  # 置0
    w2.grad.data.zero_()

使用 nn.Module 自定义传播函数来搭建神经网络

import torch
from torch.autograd import Variable

batch_n = 100
hidden_layer = 100
input_data = 1000
output_data = 10


class Model(torch.nn.Module):
    def __init__(self):
        super(Model, self).__init__()

    def forward(self, input_n, w1, w2):
        x = torch.mm(input_n, w1)
        x = torch.clamp(x, min=0)
        x = torch.mm(x, w2)
        return x

    def backward(self):
        pass


model = Model()

x = Variable(torch.randn(batch_n, input_data),
             requires_grad=False)  # requires_grad = False不保留梯度
y = Variable(torch.randn(batch_n, output_data), requires_grad=False)
w1 = Variable(torch.randn(input_data, hidden_layer),
              requires_grad=True)  # requires_grad = True自动保留梯度
w2 = Variable(torch.randn(hidden_layer, output_data), requires_grad=True)

epoch_n = 20
learning_rate = 1e-6

for epoch in range(epoch_n):
    y_pred = model(x, w1, w2)
    loss = (y_pred-y).pow(2).sum()
    print("Epoch:{},Loss:{:.4f}".format(epoch, loss))
    loss.backward()  # 后向传播计算

    w1.data -= learning_rate*w1.grad.data
    w2.data -= learning_rate*w2.grad.data

    w1.grad.data.zero_()  # 置0
    w2.grad.data.zero_()

Dataset 数据集

torch.utils.data.Dataset是代表这一数据的抽象类,可以自己定义数据类继承和重写这个抽象类,只需要定义__len____getitem__函数即可。

from torch.utils.data import Dataset


class myDataset(Dataset):
    def __init__(self, csv_file, txt_file, root_dir, other_file):
        self.csv_data = pd.read_csv(csv_file)
        with open(txt_file, 'r') as f:
            data_list = f.readlines()
        self.txt_data = data_list
        self.root_dir = root_dir

    def __len__(self):
        return len(self.csv_data)

    def __getitem__(self, idx):
        data = (self.csv_data[idx], self.txt_data[idx])
        return data

通过上面的方式,可以定义需要的数据类,可以通过迭代的方法取得每一个数据,但是这样很难实现取 batchshuffle 或者多线程去读取数据,所以 Pytorch 中提供了 torch.utils.data.DataLoader 来定义一个新迭代器。

from torch.utils.data import DataLoader
dataiter = DataLoader(myDataset, batch_size=32)

nn.Module 模组

所有的层结构和损失函数来自 torch.nn

from torch import nn


class net_name(nn.Module):
    def __init__(self, other_arguments):
        super(net_name, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size)

    def forward(self, x):
        x = self.conv1(x)
        return x

一个神经网络的典型训练过程如下:

  • 定义包含一些可学习参数(或者叫权重)的神经网络
  • 在输入数据集上迭代
  • 通过网络处理输入
  • 计算 loss (输出和正确答案的距离)
  • 将梯度反向传播给网络的参数
  • 更新网络的权重,一般使用一个简单的规则:weight = weight - learning_rate * gradient

使用 torch.nn 内的序列容器 Sequential

import torch

batch_n = 100
hidden_layer = 100
input_data = 1000
output_data = 10


model = torch.nn.Sequential(
    torch.nn.Linear(input_data, hidden_layer),
    torch.nn.ReLU(),
    torch.nn.Linear(hidden_layer, output_data)
)


print(model)

使用 nn.Module 定义一个神经网络

import torch
import torch.nn as nn
import torch.nn.functional as F


class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        # 输入图像channel:1, 输出channel:6, 5x5卷积核
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        # an affine operation: y = Wx + b
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # 2x2 Max pooling
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # 如果是方阵,则可以只使用一个数字进行定义
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, self.num_flat_features(x))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

    def num_flat_features(self, x):
        size = x.size()[1:]  # 除去批处理维度的其他所有维度
        num_features = 1
        for s in size:
            num_features *= s
        return num_features


net = Net()
print(net)

torch.optim 优化

优化算法分为两大类:

(1)一阶优化算法
使用各个参数的梯度值来更新参数,最常用的是梯度下降。梯度下降的功能是通过寻找最小值,控制方差,更新模型参数,最终使模型收敛,网络的参数更新公式:

w = w − η ∂ L ∂ w w = w - \eta \frac{\partial L}{\partial w} w=wηwL

其中, η \eta η是学习率, ∂ L ∂ w \frac{\partial L}{\partial w} wL是损失函数关于参数 w w w的梯度。

(2)二阶优化算法
二阶优化算法使用了二阶导数(Hessian方法)来最小化或最大化损失函数,主要是基于牛顿法:

w = w − η H − 1 ∂ L ∂ w w = w - \eta H^{-1} \frac{\partial L}{\partial w} w=wηH1wL

其中, H H H是损失函数关于参数 w w w的Hessian矩阵。

# 优化器(SGD)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

模型的保存和加载

#保存完整模型
torch.save(model,path)

#保存模型的状态
torch.save(model.state_dict(),path)
#加载完整模型
model = torch.load(path)

#加载模型的状态,需要先定义模型结构
model.load_state_dict(torch.load(path))

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

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

相关文章

【职工管理系统】C++全栈体系(十五)

职工管理系统 第十一章 添加职工 功能描述&#xff1a;按照职工的编号进行删除职工操作 一、删除职工函数声明 在workerManager.h中添加成员函数 void Del_Emp(); //删除职工void Del_Emp();二、职工是否存在函数声明 很多功能都需要用到根据职工是否存在来进行操作如&…

基于Android的综合物流系统

需求信息&#xff1a; &#xff08;1&#xff09;后台管理平台 设计实现物流管理平台&#xff0c;完成对司机、所载货物、出发点、目的地、运输轨迹等的管理。主要研究的内容为前后端框架的选择&#xff0c;对系统信息的展示与管理。 数据展示&#xff1a;管理人员通过不同的筛…

Redis持久化 | 黑马Redis高级篇

目录 RDB持久化 1、介绍 2、命令 3、配置 4、bgsave的fork底层原理 5、总结 AOF持久化 1、介绍 2、开启 3、三种写回策略 4、AOF后台重写 混合持久化 总结 Redisd的持久化有两种方式&#xff0c;分别是RDB和AOF RDB持久化 1、介绍 RDB&#xff0c;Redis数据备份…

软件测试项目实战【不爱听书】测试全套教程以及源码

前言 软件测试流程&#xff1a;需求分析—>测试计划—>测试设计—>测试执行—>测试报告 一、需求分析 “不爱听书”是一个为用户提供创作音乐和收听音频的平台。对于该项目的需求分析&#xff0c;提炼出相关测试点。 基本功能需求 用户可以进行注册、登录与退…

使用PyTorch构建GAN生成对抗网络源码(详细步骤讲解+注释版)02 人脸识别 下

文章目录1 测试鉴别器2 建立生成器3 测试生成器4 训练生成器5 使用生成器6 内存查看上一节&#xff0c;我们已经建立好了模型所必需的鉴别器类与Dataset类。使用PyTorch构建GAN生成对抗网络源码&#xff08;详细步骤讲解注释版&#xff09;02 人脸识别 上接下来&#xff0c;我们…

Source Insight基本使用

据说阅读Linux源码经常使用此工具&#xff1b;先看一下基本使用&#xff1b; 新建一个工程&#xff1b; OK以后出现下图&#xff1b;这是insight项目的目录&#xff1b; 把要阅读的源码工程加进来&#xff1b; 如下2个选项选中&#xff0c;OK&#xff1b; 如果下图右侧的内容没…

在Windows中操作系统下,检查Python脚本是否已运行

在Windows中操作系统下&#xff0c;检查Python脚本是否已运行 作者&#xff1a;虚坏叔叔 博客&#xff1a;https://xuhss.com 早餐店不会开到晚上&#xff0c;想吃的人早就来了&#xff01;&#x1f604; 一、原理 用一个空的虚拟文件。 在进程开始时&#xff0c;检查文件是…

ruby 给钉钉群发消息

给钉钉群发一条工作消息用途如下: Ipa, apk,打包完成了, 可以用作测试群表格导出成功了, 一般的群消息比如后台日志报警等等 步骤如下 群设置 - 智能群助手 - 添加机器人 - 选择 - 自定义 机器人设置里面要设置一个自定义关键词, 比如这里面 我起个名字 summerxx 上篇说到我…

javaweb高校大学毕业生就业跟踪系统ssm idea maven

系统所要实现的功能分析&#xff0c;对于现在网络方便的管理&#xff0c;系统要实现毕业生可以直接在平台上进行查看所有数据信息&#xff0c;根据需求可以进行在线添加&#xff0c;删除或修改企业信息、问卷调查、问卷提交、招聘信息、投递简历、企业评价、就业调查、就业表提…

(十三)devops持续集成开发——jenkins流水线发布一个sonar qube质量检查项目

前言 在前面的内容中我们已经介绍过如何在jenkins中集成质量检查工具sonar qube&#xff0c;以及sonar qube服务的安装。本节内容我们通过使用jenkins构建一个包含sonar qube质量检查的流水线项目&#xff0c;从而实现项目部署发布上线前的代码质量检查。从而保证系统的稳定性…

带约束进化算法问题分析Constrained Evolutionary Algorithms

经典论文《Evolutionary Algorithms for Constrained Parameter Optimization Problems》对带约束的进化算法进行了综述&#xff0c;本文不涉及其内容的翻译&#xff0c;主要为个人对论文理解和思考。 1. 进化算法定义Evolutionary Algorithms 论文中所讨论的进化算法主要为以…

java泛型5

泛型类 Java泛型不仅允许在使用通配符形参时设定上限&#xff0c;而且可以在定义泛型形参时设定上限&#xff0c;用于表示传给该泛型形参的实际类型要么是该上限类型&#xff0c;要么是该上限类型的子类。 上面程序定义了一个Apple泛型类&#xff0c;该Apple类的泛型形参的上限…

免安装PortableGit配置 + TortoiseGit安装

文章目录官网/安装Git将git命令添加到Path环境变量添加GitHub登录账号下载安装TortoiseGit官网/安装Git Git官网&#xff1a;https://git-scm.com/ 国内用户&#xff0c;建议通过淘宝镜像网站下载安装文件&#xff1a; https://registry.npmmirror.com/binary.html?pathgit…

18.异常

目录 一.异常 1.1 什么是异常 1.2 为什么要学习异常 1.3 异常的体系 1.5 编译时异常 1.5.1 什么是编译时异常 1.5.2 编译时异常的作用 1.5.3 常见编译时异常 1.6 运行时异常 1.6.1 什么是运行时异常 1.6.2 常见运行时异常 1.6 异常的默认处理流程&#xff08;RunTim…

web3:区块链常见的几大共识机制及优缺点

web3相关学习一并收录至该博客&#xff1a;web3学习博客目录大全 胡歌看了都得给我一键三连吧&#xff01; 目录什么是共识&#xff1f;什么是共识机制&#xff1f;共识机制的目标为什么需要共识机制?如何评价一个共识机制的优劣:共识机制分类PoW( Proof of Work)工作量证明&a…

2023年最新!北京Java培训机构排行榜新鲜出炉!

北京作为中国的首都&#xff0c;其人才的需求的体量之大是其他城市不可比的。那么在北京学习Java&#xff0c;到底该怎么选择Java培训机构哪?怎么在众多的机构里面选择出最适合自己的哪&#xff1f;下面是小编根据口碑和实力整理出的北京Java培训机构排行榜单&#xff0c;仅供…

【Effective_Objective-C_6 块block】

文章目录前言GCD和块的简介37.理解块的概念块的基础知识块可以捕获变量内联块的用法块的内部结构全局块&#xff0c;栈块&#xff0c;堆块堆块全局块要点38.为常用的块类型创建typedef要点39.用handler块降低代码分散程度协议传值实现异步块实现异步回调操作里的块要点40.用块引…

说说redux的实现原理是什么,写出核心代码?

目录标题一、redux三大基本原则是&#xff1a;二、实现原理&#xff1a;三、如何使用一、redux三大基本原则是&#xff1a; 单一数据源state是只读的使用纯函数来执行修改 注意的是&#xff0c;redux并不是只应用在react中&#xff0c;还与其他界面库一起使用&#xff0c;如V…

3.26 haas506 2.0开发教程-example- 串口控制ESP32-CAM OV2640拍照

haas506串口控制ESP32-CAM OV2640拍照介绍ESP32-CAM开发板硬件连接代码流程代码ESP32-CAM开发板代码HaaS506开发板代码测试ESP32-CAM开发板测试介绍 通过HaaS506串口发送指令&#xff0c;控制ESP32-CAM进行拍照&#xff0c;并将照片储存在SD卡中。ESP32-CAM需要5V供电才能正常…

小程序开发常见问题总结(超实用)

小程序开发常见问题总结&#xff08;超实用&#xff09; 文章目录小程序开发常见问题总结&#xff08;超实用&#xff09;1.小程序user agent stylesheet问题。2.this.setData is not function错误3.flex布局3.1flex布局原理3.2flex父项属性3.3flex布局子项元素4.自定义组件1.在…