【2-神经网络优化】北京大学TensorFlow2.0

news2024/12/27 16:08:53

课程地址:【北京大学】Tensorflow2.0_哔哩哔哩_bilibili

Python3.7和TensorFlow2.1

六讲:

  1. 神经网络计算:神经网络的计算过程,搭建第一个神经网络模型

  1. 神经网络优化:神经网络的优化方法,掌握学习率、激活函数、损失函数和正则化的使用,用Python语言写出SGD、Momentum、Adagrad、RMSProp、Adam五种反向传播优化器

  1. 神经网络八股:神经网络搭建八股,六步法写出手写数字识别训练模型

  1. 网络八股扩展:神经网络八股扩展,增加自制数据集、数据增强、断点续训、参数提取和acc/loss可视化,实现给图识物的应用程序

  1. 卷积神经网络:用基础CNN、LeNet、AlexNet、VGGNet、InceptionNet和ResNet实现图像识别

  1. 循环神经网络:用基础RNN、LSTM、GRU实现股票预测


预备知识-几个函数

tf.where(条件语句,真返回A,假返回B):条件语句真返回A,条件语句假返回B

tf.where(tf.greater(a,b),a,b)   # 若a>b,返回a对应位置的元素,否则返回b对应位置的元素

np.random.RandomState.rand(维度):返回一个[0,1)之间的随机数。若维度为空,返回标量

import numpy as np
rdm = np.random.RandomState(seed=1)  # seed=常数,每次生成随机数相同
a = rdm.rand()   # 返回一个随机标量
b = rdm.rand(2,3)   # 返回维度为2行3列的随机数矩阵

np.vstack(数组1,数组2):将两个数组按垂直方向叠加

import numpy as np
a = np.array([1,2,3])
b = np.array([4,5,6])
c = np.vstack((a,b))  # 注意要加括号,因为该函数的输入参数只有1个
# c:
# [[1 2 3]
# [4 5 6]]

以下三个函数经常一起使用,生成网格坐标点:

np.mgrid[起始值:结束值:步长, 起始值:结束值:步长, ...]:返回若干组维度相同的等差数组,且 [起始值 结束值) 为左闭右开。第一个“起始值:结束值:步长”决定了二维数组的行,第二个“起始值:结束值:步长”决定了二维数组的列

为了产生坐标对,把x(网格交点的x坐标)和y(网格交点的y坐标)组合起来,是一个二维有序数组,且对应位置就是它们的坐标

import numpy as np
x,y = np.mgrid[1:3:1,2:4:0.5]

x.ravel():将x变为一维数组,把.前的变量拉直

np.c_[数组1, 数组2, ...]:使返回的间隔数值点配对

grid = np.c_[x.ravel(),y.ravel()]

神经网络复杂度

分为时间复杂度和空间复杂度

时间复杂度

即模型的运算次数,用乘加运算次数衡量。每个具有计算能力的神经元小球都要收集前一层的每一个输入特征,乘以各自线上的权重,再加上这个神经元的偏置项b,有几条权重线就有几次乘加运算

空间复杂度

用神经网络层数和神经网络中待优化的参数个数表示。计算神经网络层数时,只统计具有运算能力的层:输入层仅把数据传输过来,没有运算,故在统计神经网络层数时,不算输入层;输入层和输出层之间的所有层都叫隐藏层


学习率lr

表征了参数每次更新的幅度

指数衰减学习率

实际应用中,可以先用较大的学习率,快速得到较优解,然后逐步减小学习率,使模型在训练后期稳定

指数衰减学习率,根据当前迭代次数,动态改变学习率的值,一般写在for循环中

绿色的为超参数

  • 初始学习率:最初的学习率

  • 学习率衰减率:每次学习率按照这个比例指数衰减

  • 当前轮数:变量,是个计数器,可以用当前迭代了多少次数据集,即epoch的数值表示;也可以用当前一共迭代了多少次batch,即global_step表示

  • 多少轮衰减一次:迭代多少次数据集,或迭代多少次batch更新一次学习率,决定了学习率更新的频率

框起来的4行是补充的4行

import tensorflow as tf

w = tf.Variable(tf.constant(5, dtype=tf.float32))

epoch = 40
LR_BASE = 0.2  # 最初学习率
LR_DECAY = 0.99  # 学习率衰减率
LR_STEP = 1  # 喂入多少轮BATCH_SIZE后,更新一次学习率

for epoch in range(epoch):  # for epoch 定义顶层循环,表示对数据集循环epoch次,此例数据集数据仅有1个w,初始化时constant赋值为5,循环40次迭代
    lr = LR_BASE * LR_DECAY ** (epoch / LR_STEP)
    with tf.GradientTape() as tape:  # with结构到grads框起了梯度的计算过程
        loss = tf.square(w + 1)
    grads = tape.gradient(loss, w)  # .gradient函数告知谁对谁求导

    w.assign_sub(lr * grads)  # .assign_sub 对变量做自减 即:w -= lr*grads 即 w = w - lr*grads
    print("After %s epoch,w is %f,loss is %f,lr is %f" % (epoch, w.numpy(), loss, lr))

激活函数

对于线性函数,即使有多层神经元首尾相接,构成深层神经网络,依旧是线性组合,模型的表达力不够。激活函数是用来加入非线性因素,因为线性模型的表达能力不够。引入非线性激活函数,可使深层神经网络的表达能力更加强大

优秀的激活函数应满足:

  1. 非线性:激活函数非线性时,多层神经网络可逼近所有函数。只有当激活函数是非线性时,才不会被单层网络替代,使多层网络有了意义

  1. 可微性:优化器大多用梯度下降更新参数,若不可微,就无法更新参数了

  1. 单调性:当激活函数是单调的,能保证单层网络的损失函数是凸函数,更容易收敛

  1. 近似恒等性:f(x)≈x,当参数初始化为随机小值时,神经网络更稳定

激活函数输出值的范围:

  1. 为有限值时:权重对特征的影响会更显著,基于梯度的优化方法(梯度下降法更新参数)更稳定

  1. 为无限值时:参数的初始值对模型的影响非常大,建议调小学习率

sigmoid tf.nn.sigmoid(x)

sigmoid函数把输入值变换到0-1之间输出,若输入值是非常大的负数,输出的就是0;若输入值是非常大的正数,输出的就是1,相当于对输入进行了归一化

缺点:

  1. 易造成梯度消失:深层网络更新参数时,需要从输出层到输入层逐层进行链式求导,而sigmoid的导数输出是0-0.25间的小数,链式求导需要多层导数连续相乘,会出现多个0-0.25之间的连续相乘,结果将趋于0,产生梯度消失,使得参数无法继续更新

  1. 输出非0均值,收敛慢:希望输入每层神经网络的特征是以0为均值的小数值,但是过sigmoid激活函数后的数据都是正数,会使收敛变慢

  1. 幂运算复杂,训练时间长:sigmoid函数存在幂运算,计算复杂度大,训练时间长

sigmoid函数可应用在训练过程中

  • sigmoid函数只能处理两个类(多分类问题用softmax)

  • softmax函数大都运用在神经网络中的最后一层,使值在(0,1)之间

tanh tf.math.tanh(x)

该激活函数的输出值为零均值,但依旧存在梯度消失幂运算(复杂,训练时间长)的问题

比sigmoid函数收敛速度快

ReLU tf.nn.relu(x)

分段函数,符合“激活函数具备近似恒等性”

优点:

  1. 在正区间内解决了梯度消失问题

  1. 使用时只需要判断输入是否>0,计算速度快

  1. 训练参数时的收敛速度比sigmoid和tanh快

缺点:

  • 输出非0均值,收敛慢

  • dead relu问题:某些神经元可能永远不会被激活,导致相应的参数永远不能被更新

送入激活函数的输入特征是负数时,激活函数输出是0,反向传播得到的梯度是0,导致参数无法更新,造成神经元死亡。造成神经元死亡的根本原因是经过relu函数的负数特征过多导致。可以:

  • 改进随机初始化,避免过多的负数特征送入relu函数;

  • 通过设置更小的学习率,减少参数分布的巨大变化,避免训练中产生过多负数特征进入relu函数

Leaky ReLU tf.nn.leaky_relu(x)

为解决relu负区间为0,引起神经元死亡问题而设计的。Leaky ReLU负区间引入了一个固定的斜率α,使其负区间不再恒等于0

虽然Leaky ReLU比relu效果更好,但实际使用中选择relu作激活函数的网络会更多

softmax tf.nn.softmax(x)

对神经网络全连接层输出进行变换,使其服从概率分布,即每个值都位于[0,1],且和为1

总结


损失函数loss

前向传播计算出的结果y与已知标准答案y_的差距

神经网络的优化目标就是找到某套参数,使得计算出的结果y和已知标准答案y_无限接近

均方误差MSE(Mean Squared Error)

回归问题最常用的损失函数。回归问题解决的是对具体数值的预测,如房价预测、销量预测,这些问题需要预测的不是一个事先定义好的类别,而是一个任意实数

tf.reduce_mean(tf.square(y_-y))

例:预测酸奶日销量y(即产量),影响日销量的因素为x1、x2,销量y_(即已知答案,最佳情况是产量=销量)

随机生成x1和x2,拟造数据集y_=x1+x2,添加-0.05~0.05的噪声。拟合可以预测销量的函数

# 自制数据集,构建一层神经网络,预测酸奶日销量
import tensorflow as tf
import numpy as np

SEED = 23455   # 随机种子,保证每次生成的数据集一样(实际应用中不写SEED)

rdm = np.random.RandomState(seed=SEED)  # 生成[0,1)之间的随机数
x = rdm.rand(32, 2)  # 生成32行2列的输入特征x,包含了32组0-1之间的随机数x1和x2

# .rand()生成[0,1)之间的随机数
# 生成标准答案y_
y_ = [[x1 + x2 + (rdm.rand() / 10.0 - 0.05)] for (x1, x2) in x]  # 生成噪声[0,1)/10=[0,0.1); [0,0.1)-0.05=[-0.05,0.05)
x = tf.cast(x, dtype=tf.float32)   # x转变数据类型

w1 = tf.Variable(tf.random.normal([2, 1], stddev=1, seed=1))  # 随机初始化参数w1,初始化为两行一列

epoch = 15000   # 数据集迭代次数
lr = 0.002

for epoch in range(epoch):
    with tf.GradientTape() as tape:
        y = tf.matmul(x, w1)   # for循环中用with结构,求前向传播计算结果y
        loss_mse = tf.reduce_mean(tf.square(y_ - y))   # 求均方误差损失函数loss_mse
    grads = tape.gradient(loss_mse, w1)   # 损失函数对待训练参数w1求偏导
    w1.assign_sub(lr * grads)   # 更新参数w1

    if epoch % 500 == 0:   # 每迭代500轮数据打印当前参数w1
        print("After %d training steps,w1 is " % (epoch))
        print(w1.numpy(), "\n")
print("Final w1 is: ", w1.numpy())

拟合结果为y=1.000972x1+0.9977485x2,与构造数据集的y=x1+x2一致,说明拟合正确

使用均方误差作为损失函数,默认认为销量预测的多了或者少了,损失是一样的,但真实情况并非如此

自定义损失函数

根据具体任务和目的,可设计不同的损失函数。损失函数的定义能极大影响模型预测效果,好的损失函数设计对于模型训练能够起到良好的引导作用

预测商品销量,预测多了损失成本,预测少了损失利润,而利润往往≠成本,这种情况下使用均方误差计算loss,无法使利益最大化

loss = tf.reduce_sum(tf.where(tf.greater(y, y_), (y - y_) * COST, (y_ - y) * PROFIT))

例:预测酸奶销量,假设酸奶成本COST=1元,酸奶利润PROFIT=99元。预测少了损失利润99元,预测多了损失成本1元,显然预测少了损失大,希望拟合的函数往多了预测

# 希望拟合的函数尽量往多了预测,用自定义损失函数拟合出来的预测酸奶日销量会不会智能地往多了预测?
# 以下代码相比上面,仅改动了loss函数
import tensorflow as tf
import numpy as np

SEED = 23455
COST = 1
PROFIT = 99

rdm = np.random.RandomState(SEED)
x = rdm.rand(32, 2)
y_ = [[x1 + x2 + (rdm.rand() / 10.0 - 0.05)] for (x1, x2) in x]  # 生成噪声[0,1)/10=[0,0.1); [0,0.1)-0.05=[-0.05,0.05)
x = tf.cast(x, dtype=tf.float32)

w1 = tf.Variable(tf.random.normal([2, 1], stddev=1, seed=1))

epoch = 10000
lr = 0.002

for epoch in range(epoch):
    with tf.GradientTape() as tape:
        y = tf.matmul(x, w1)
        # 只改动了损失函数:当预测的y多了时损失成本,当预测的y少了时损失利润
        loss = tf.reduce_sum(tf.where(tf.greater(y, y_), (y - y_) * COST, (y_ - y) * PROFIT))

    grads = tape.gradient(loss, w1)
    w1.assign_sub(lr * grads)

    if epoch % 500 == 0:
        print("After %d training steps,w1 is " % (epoch))
        print(w1.numpy(), "\n")
print("Final w1 is: ", w1.numpy())

# 自定义损失函数
# 酸奶成本1元,酸奶利润99元
# 成本很低,利润很高,人们希望多预测些,生成模型系数大于1,往多了预测

可见拟合出的参数均>1,偏大,模型的确在尽量往多了预测

目标检测中的多种损失函数
目标检测的主要功能是 定位识别,损失函数的功能就是让定位更精确、识别准确率更高
目标检测任务的损失函数由 分类损失(交叉熵、softmax loss、logloss、focal loss等)和 回归损失(如Smooth L1 Loss、loU Loss、GloU Loss、DloU Loss & CloU Loss等)两部分构成

交叉熵损失函数CE(Cross Entropy)

表征两个概率分布之间的距离。通过交叉熵的值可以判断哪个预测结果与标准答案更接近,交叉熵越小说明两者分布越接近。是分类问题中常用的损失函数

tf.losses.categorical_crossentropy(y_,y)
# 传入的y_和y为列表或array

softmax与交叉熵结合

在执行分类问题时,通常先用softmax函数让输出结果符合概率分布,再求y和y_的交叉熵损失函数

# TensorFlow给出了一个可同时计算概率分布和交叉熵的函数
tf.nn.softmax_cross_entropy_with_logits(y_,y)
# 等同于下面两句:
# y_pro = tf.nn.softmax(y)
# tf.losses.categorical_crossentropy(y_,y_pro)

欠拟合与过拟合

欠拟合

模型不能有效拟合数据集,对现有数据集学习的不够彻底

解决方法:

  • 增加输入特征项:给网络更多维度的输入特征

  • 增加网络参数:扩展网络规模,增加网络深度,提升模型表达力

  • 减少正则化参数

过拟合

模型对当前数据拟合的太好了,但对从未见过的新数据却难以做出正确的判断,缺乏泛化力

解决方法:

  • 数据清洗:减少数据集中的噪声,使数据集更纯净

  • 增大训练集:让模型见到更多数据

  • 采用正则化:一种通用的、有效的方法

  • 增大正则化参数

正则化缓解过拟合

正则化在损失函数中引入模型复杂度指标,利用给参数w加权值,弱化了训练数据的噪声(一般只对w使用正则化,不正则化偏置b)

正则化的选择:

  1. L1正则化大概率会使很多参数变为0,因此该方法可通过稀疏参数,即减少参数的数量,降低复杂度

  1. L2正则化会使参数很接近0但不为0,因此该方法可通过减小参数值的大小,降低复杂度,可以有效缓解数据集中因噪声引起的过拟合

例:

dot.csv

未加L2正则化的代码:

# 导入所需模块
import tensorflow as tf
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd

# 读入数据/标签,生成x_train y_train
df = pd.read_csv('dot.csv')
x_data = np.array(df[['x1', 'x2']])  # 取出x1和x2两列,并转成二维数组
y_data = np.array(df['y_c'])  # 取出y_c列,并转成列表

# np.vstack(数组1,数组2):将两个数组按垂直方向叠加
x_train = np.vstack(x_data).reshape(-1, 2)
y_train = np.vstack(y_data).reshape(-1, 1)

Y_c = [['red' if y else 'blue'] for y in y_train]   # 1的话红点,0的话蓝点
# Y_c打印出来是一个二维数组,里面是['red']['blue']这样的元素

# 转换x的数据类型,否则后面矩阵相乘时会因数据类型问题报错
x_train = tf.cast(x_train, tf.float32)
y_train = tf.cast(y_train, tf.float32)

# from_tensor_slices函数切分传入的张量的第一个维度,生成相应的数据集,使输入特征和标签值一一对应
train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)

# 生成两层神经网络的参数,输入层为2个神经元,隐藏层为11个神经元(随便选的),1层隐藏层,输出层为1个神经元
# 用tf.Variable()保证参数可训练
w1 = tf.Variable(tf.random.normal([2, 11]), dtype=tf.float32)   # 2个输入特征
b1 = tf.Variable(tf.constant(0.01, shape=[11]))

w2 = tf.Variable(tf.random.normal([11, 1]), dtype=tf.float32)  # 第二层网络输入特征的个数11与第一层网络的输出个数11要一致
b2 = tf.Variable(tf.constant(0.01, shape=[1]))   # 整个神经网络的输出节点个数要和标签一样,数据集中每组x1、x2对应一个标签

# 定义超参数
lr = 0.005  # 学习率
epoch = 800  # 循环轮数

# 训练部分
# for循环嵌套,反向传播求梯度,更新参数
for epoch in range(epoch):   # epoch是对数据集的循环计数
    for step, (x_train, y_train) in enumerate(train_db):   # step是对batch的循环计数
        # 在with结构中完成前向传播计算出y
        with tf.GradientTape() as tape:  # 记录梯度信息

            h1 = tf.matmul(x_train, w1) + b1  # 记录神经网络乘加运算
            h1 = tf.nn.relu(h1)
            y = tf.matmul(h1, w2) + b2

            # 采用均方误差损失函数mse = mean(sum(y-out)^2)
            loss = tf.reduce_mean(tf.square(y_train - y))

        # 计算loss对各个参数的梯度
        variables = [w1, b1, w2, b2]
        grads = tape.gradient(loss, variables)  # 损失函数loss分别对w1/b1/w2/b2求偏导数

        # 实现梯度更新,分别更新w1/b1/w2/b2
        # w1 = w1 - lr * w1_grad
        # tape.gradient是自动求导结果与[w1, b1, w2, b2]  索引为0,1,2,3
        w1.assign_sub(lr * grads[0])
        b1.assign_sub(lr * grads[1])
        w2.assign_sub(lr * grads[2])
        b2.assign_sub(lr * grads[3])

    # 每20个epoch,打印loss信息
    if epoch % 20 == 0:
        print('epoch:', epoch, 'loss:', float(loss))

# 预测部分
print("*******predict*******")

# xx在-3到3之间以步长为0.1,yy在-3到3之间以步长0.1,生成间隔数值点
xx, yy = np.mgrid[-3:3:.1, -3:3:.1]   # 生成网格坐标点,密度是0.1
'''
xx:
[[-3.  -3.  -3.  ... -3.  -3.  -3. ]
 [-2.9 -2.9 -2.9 ... -2.9 -2.9 -2.9]
 [-2.8 -2.8 -2.8 ... -2.8 -2.8 -2.8]
 ...
 [ 2.7  2.7  2.7 ...  2.7  2.7  2.7]
 [ 2.8  2.8  2.8 ...  2.8  2.8  2.8]
 [ 2.9  2.9  2.9 ...  2.9  2.9  2.9]]
'''
# 将xx , yy拉直,并合并配对为二维张量,生成二维坐标点
grid = np.c_[xx.ravel(), yy.ravel()]
'''
xx.ravel():
[-3.  -3.  -3.  ...  2.9  2.9  2.9]

grid:
[[-3.  -3. ]
 [-3.  -2.9]
 [-3.  -2.8]
 ...
 [ 2.9  2.7]
 [ 2.9  2.8]
 [ 2.9  2.9]]
'''

grid = tf.cast(grid, tf.float32)
'''
grid:
tf.Tensor(
[[-3.  -3. ]
 [-3.  -2.9]
 [-3.  -2.8]
 ...
 [ 2.9  2.7]
 [ 2.9  2.8]
 [ 2.9  2.9]], shape=(3600, 2), dtype=float32)
'''

# 将网格坐标点喂入训练好的神经网络,神经网络会为每个坐标输出一个预测值,probs为输出
# 要区分输出偏向1还是0,可以把预测结果为0.5的线标出颜色,即为0和1的区分线
probs = []
for x_test in grid:
    # 使用训练好的参数进行预测
    h1 = tf.matmul([x_test], w1) + b1
    h1 = tf.nn.relu(h1)
    y = tf.matmul(h1, w2) + b2   # y为预测结果
    probs.append(y)

# 取第0列给x1,取第1列给x2
x1 = x_data[:, 0]
x2 = x_data[:, 1]

# probs的shape调整成xx的样子
probs = np.array(probs).reshape(xx.shape)

# 画出x1和x2的散点
plt.scatter(x1, x2, color=np.squeeze(Y_c))  # squeeze去掉纬度是1的纬度,相当于去掉[['red'],['blue']]内层括号,变为['red','blue']

# 把坐标xx yy和对应的值probs放入contour函数,给probs值为0.5的所有点上色  plt.show()后 显示的是红蓝点的分界线
'''
plt.contour(X, Y, Z, [levels])  作用:绘制轮廓线,类似于等高线
X和Y就是坐标位置  Z代表每个坐标对应的高度值,是一个二维数组
levels传入一个包含高度值的一维数组,这样便会画出传入的高度值对应的等高线
'''
plt.contour(xx, yy, probs, levels=[.5])   # 画出预测值y=0.5的曲线
plt.show()

# 读入红蓝点,画出分割线,不包含正则化   结果:轮廓不够平滑,存在过拟合现象
# 不清楚的数据,建议print出来查看

轮廓不够平滑,存在过拟合现象

加了L2正则化的代码:

# 相比p29_regularizationfree.py,仅在with结构加入了L2正则化

        with tf.GradientTape() as tape:  # 记录梯度信息

            h1 = tf.matmul(x_train, w1) + b1  # 记录神经网络乘加运算
            h1 = tf.nn.relu(h1)
            y = tf.matmul(h1, w2) + b2

            # 采用均方误差损失函数mse = mean(sum(y-out)^2)
            loss_mse = tf.reduce_mean(tf.square(y_train - y))
           
            # 添加l2正则化
            loss_regularization = []
            # tf.nn.l2_loss(w)=sum(w ** 2) / 2
            loss_regularization.append(tf.nn.l2_loss(w1))
            loss_regularization.append(tf.nn.l2_loss(w2))
            # 求和
            # 例:x = tf.constant(([1,1,1],[1,1,1]))
            #    tf.reduce_sum(x)
            # >>>6
            loss_regularization = tf.reduce_sum(loss_regularization)
            # 总loss = 衡量预测值与标准答案差距的均方误差loss_mse + 各个参数w权重的正则化loss_regularization
            loss = loss_mse + 0.03 * loss_regularization  # REGULARIZER = 0.03

加了L2正则化后的曲线更平缓,有效缓解了过拟合


优化器更新网络参数

用python编写反向传播优化器,实现参数更新。主要有以下几类优化器:

  • SGD(with No Momentum)

  • SGD(with Momentum)

  • AdaGrad

  • RMSProp

  • Adam

未完待续...

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

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

相关文章

7. 字符串str的详细讲解

python3字符串str的使用 (1) 基本使用 [a]. Python 中单引号 和双引号 " 使用完全相同; [b]. 使用三引号(单或双)可以指定一个多行字符串; # 长字符串 print( jkl fsf fs fs )[c]. 反斜杠可以用来转义,使用r(raw)可以让反斜杠…

【LeetCode高频100题-3】冲冲冲(持续更新23.1.19)

文章目录62. 不同路径题意解法1 排列组合解法2 动态规划62. 不同路径 题意 一道数学题,排列组合/小学奥赛题。动态规划不是一般来解决最值问题的吗,这道题为什么会想到dp? 解法1 排列组合 从左上角到右下角,一共要走mn-2步&am…

DEJA_VU3D - Cesium功能集 -完整地图标绘及编辑功能系列预告

前言编写这个专栏主要目的是对工作之中基于Cesium实现过的功能进行整合,有自己琢磨实现的,也有参考其他大神后整理实现的,初步算了算现在有差不多实现小140个左右的功能,后续也会不断的追加,所以暂时打算一周2-3更的样…

【算法】克鲁斯卡尔 (Kruskal) 算法

目录1.概述2.代码实现2.1.并查集2.2.邻接矩阵存储图2.3.邻接表存储图2.4.测试代码3.应用本文参考: 《数据结构教程》第 5 版 李春葆 主编 1.概述 (1)在一给定的无向图 G (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边&#xf…

【6s965-fall2022】剪枝✂pruningⅠ

模型剪枝的介绍 修剪,消除不必要的知识。DNN的知识可以理解为存在于其权重中。 事实证明,许多 DNN 模型可以被分解为权重张量,而权重张量经常包含统计冗余(稀疏性)。因此,你可以压缩 DNN 的权重张量&…

[从零开始]用python制作识图翻译器·五

测试 通过以上步骤我们终于实现了系统,现在到了紧张刺激的测试环节。直接运行run.py文件: python run.py ::注意需要进入conda环境稍作等等,我们的系统就运行啦(啵唧啵唧)。 在使用之前,我们还需要在设置中输入自己的…

使用vscode进行C++代码开发(linux平台)

使用vscode进行C代码开发(linux平台一、插件安装二、常用快捷键三、重要配置文件四、实际例子1. 编译并运行一个含有多个文件夹和文件的代码工程2. 编译并运行一个依赖第三方库的代码工程参考资料一、插件安装 执行 ctrl shift x打开插件窗口,然后搜索c插件&…

鸡格线(map操作)

鸡格线 (nowcoder.com) 题目描述 你有一个长为n的数组a,你需要支持以下两种操作: 1、输入l, r, k,对区间[1,r]中所有数字执行a; f(a;)操作k次(式中等号表示赋值操作),之中f(z)round(10、c),round为四舍五入函数。 2、输出当前数组…

缓存一致性问题怎么解决

关于Redis的其他的一些面试问题已经写过了,比如常见的缓存穿透、雪崩、击穿、热点的问题,但是还有一个比较麻烦的问题就是如何保证缓存一致性。对于缓存和数据库的操作,主要有以下两种方式。先删缓存,再更新数据库先删除缓存&…

Java 多线程 笔记

文章目录实现方式1. 通过Thread类2. 通过Runnable接口3. 通过Callable接口线程状态线程方法1. 线程停止2. 线程休眠sleep3. 线程礼让yield4. 线程强制执行join5. 线程状态观测getState6. 线程优先级守护线程(daemon线程同步 TODO线程死锁Lock锁(可重入锁…

Typora+Gitee+PicGo搭建图床

引言 markdown原则上不建议使用base64内嵌图片,因为太麻烦。 如果只是在本机浏览、编辑的话,那引用相对路径或者绝对路径即可,但是考虑到要发布、分享的情况,使用图床是比较好的解决方案。 本教程可以快速得到一个相对稳定的免…

单片机数据、地址、指令、控制总线结构

数据、地址、指令:之所以将这三者放在一同,是因为这三者的实质都是相同的──数字,或者说都是一串‘0’和‘1’组成的序列。换言之,地址、指令也都是数据。 指令:具体可参考文章 由单片机芯片的设计者规则的一种数字…

c程序gcc编译常见报错及解决方法整理

目录一、简介二、常见报错及解决方法1、数组定义错误2、Not enough information to produce a SYMDEFs file3、文件乱码<U0000>4、未定义或未申明报错5、代码中误加入中文三、其他相关链接一、简介 本文主要是整理c程序编译过程的常见报错的解决方法&#xff0c;方便大家…

Leetcode.312 戳气球

题目链接 Leetcode.312 戳气球 题目描述 有 n个气球&#xff0c;编号为0到 n - 1&#xff0c;每个气球上都标有一个数字&#xff0c;这些数字存在数组 nums中。 现在要求你戳破所有的气球。戳破第 i 个气球&#xff0c;你可以获得 nums[i−1]∗nums[i]∗nums[i1]nums[i - 1]…

C++ 类 对象初学者学习笔记

C 类 & 对象 访问数据成员 #include <iostream>using namespace std;class Box {public:double length; // 长度double breadth; // 宽度double height; // 高度// 成员函数声明double get(void);void set( double len, double bre, double hei ); }; // 成员…

两种在CAD中加载在线卫星影像的方法

概述 经常使用CAD的朋友应该会有这样的一个烦恼&#xff0c;就是当加载卫星图到CAD中进行绘图的时候&#xff0c;由于CAD本身的限制和电脑性能等原因&#xff0c;往往不能加载太大的地图图片到CAD内&#xff0c;这里给大家介绍两种在CAD内加载在线卫星影像的方法&#xff0c;希…

用docker部署webstack导航网站-其一

序言 认识的好朋友斥资买了一个NAS&#xff0c;并搭建了webdev和其他一些web应用&#xff0c;用于存放电子书、电影&#xff0c;并用alist搭建了一个网盘。现在他还缺少一个导航页&#xff0c;于是拖我给他做一个导航页。我欣然接受了&#xff0c;他想做一个和TBox导航&#x…

Arduino开发:网页控制ESP8266三色LED灯闪烁

根据板卡原理RGB三色LED对应引脚&#xff1a;int LEDR12、int LEDG14、int LEDB13;设置串口波特率为115200Serial.begin(115200);源代码如下所示&#xff1a;#include <ESP8266WiFi.h> // 提供 Wi-Fi 功能的库#include <ESP8266WebServer.h> // 提供网站服务器功能…

分布式共识算法随笔 —— 从 Quorum 到 Paxos

分布式共识算法随笔 —— 从 Quorum 到 Paxos 概览: 为什么需要共识算法&#xff1f; 昨夜西风凋碧树&#xff0c;独上高楼&#xff0c;望尽天涯路 复制(Replication) 是一种通过将同一份数据在复制在多个服务器上来提高系统可用性和扩展写吞吐的策略, 。常见的复制策略有主从…

Flink CDC 原理

文章目录CDC&#xff0c;Change Data Capture 变更数据捕获 目前CDC有两种实现方式&#xff0c;一种是主动查询、一种是事件接收。 主动查询&#xff1a; 相关开源产品有Sqoop、Kafka JDBC Source等。 用户通常会在数据原表中的某个字段中&#xff0c;保存上次更新的时间戳或…