机器学习简介[01/2]:简单线性回归

Python 中的机器学习简介:简单线性回归

一、说明

        简单线性回归为机器学习提供了优雅的介绍。它可用于标识自变量和因变量之间的关系。使用梯度下降,可以训练基本模型以拟合一组点以供未来预测。

二、技术背景

        这是涵盖回归、梯度下降、分类和机器学习的其他基本方面的系列文章的第一篇文章。本文重点介绍简单的线性回归,它确定了一组点的最佳拟合线,以便进行将来的预测。

2.1 最佳拟合线

        最佳拟合线是最准确地表示一组点的方程。对于给定的输入,公式的输出应尽可能接近预期输出。

        在上图中,很明显,中间线比左线或右线更适合蓝点。但是,它是最适合的路线吗?有没有第四条线更适合这些观点?这条线当然可以向上或向下移动,以确保相同数量的点落在其上方和下方。但是,可能有十几行符合此确切标准。是什么让其中任何一个成为最好的?

        值得庆幸的是,有一种方法可以使用回归在数学上确定一组点的最佳拟合线。

2.2 回归

        回归有助于识别两个或多个变量之间的关系,它采用多种形式,包括简单线性、多重线性、多项式等。为了证明这种方法的有用性,将使用简单的线性回归。

        简单线性回归尝试找到一组点的最佳拟合线。更具体地说,它标识自变量和因变量之间的关系。最佳拟合线的形式为 y = mx + b

  • x 是输入或自变量
  • m 是直线的斜率或陡度
  • b 是 y 截距
  • y 是输出或因变量

        简单线性回归的目标是确定 m 和 的值,当给定 x 时,这些值将生成最准确的 y 值。这个方程,也称为模型,也可以用机器学习术语进行评估。在等式中,w 表示“重量”:Ŷ = Xw₁+ w₀

  • X 是输入或特征
  • w₁ 是斜率
  • w₀ 是偏置或 y 截距
  • Ŷ 是预测,发音为“y-hat”

        虽然这很有用,但需要评估方程的准确性。如果它的预测很差,它就不是很有用。为此,使用成本或损失函数。

2.3 成本或损失函数

        回归需要某种方法来跟踪模型预测的准确性。给定输入,方程的输出是否尽可能接近预期输出?成本函数,也称为损失函数,用于确定方程的精度。

        例如,如果预期输出为 5,而方程输出为 18,则损失函数应表示此差值。一个简单的损失函数可以输出 13,这是这些值之间的差值。这表明模型的性能很差。另一方面,如果预期输出为 5,模型预测为 5,则损失函数应输出 0,这表明模型的性能非常出色。

执行此操作的常用损失函数是均方误差 (MSE):

        此函数用于查找模型的预测 (Ŷ) 和预期输出 (Y) 之间的差异。然后对差值进行平方,以确保输出始终为正。它跨一组大小为 n 的点执行此操作。通过将所有这些点的平方差相加并除以 n,输出是均方差(误差)。这是一种同时评估模型在所有点上的性能的简单方法。下面可以看到一个简单的例子:

 

        虽然还有无数其他损失函数同样适用于这种情况,但由于其简单性,这是机器学习中回归中最受欢迎的损失函数之一,尤其是在梯度下降方面,这将在后面解释。

为了最好地理解梯度下降的位置,可以评估一个示例。

三、预测最佳拟合线

        若要显示操作中的简单线性回归,需要数据来训练模型。这以 X 数组和 Y 数组的形式出现。对于此示例,可以手动生成数据。它可以从“蓝图”函数创建。随机性可以添加到蓝图中,模型将被强制学习底层函数。PyTorch,一个标准的机器学习库,用于实现回归。

3.1 生成数据

        首先,下面的代码使用随机整数生成器生成一个输入值数组。X 当前具有 (n 个样本,num 特征)的形状。请记住,特征是一个自变量,简单线性回归有 1。在本例中,n 将为 20。

import torch

torch.manual_seed(5)
torch.set_printoptions(precision=2)

# (n samples, features) 
X = torch.randint(low=0, high=11, size=(20, 1)) 
tensor([[ 9],
        [10],
        [ 0],
        [ 3],
        [ 8],
        [ 8],
        [ 0],
        [ 4],
        [ 1],
        [ 0],
        [ 7],
        [ 9],
        [ 3],
        [ 7],
        [ 9],
        [ 7],
        [ 3],
        [10],
        [10],
        [ 4]])

        然后可以通过 Y = 1.5X + 2 传递这些值以生成输出值,并且可以使用平均值为 0 且标准差为 1 的正态分布将这些值添加一些随机性。Y 的形状为 (n 个样本1)。

        下面的代码显示了具有相同形状的随机值。

torch.manual_seed(5)

# normal distribution with a mean of 0 and std of 1
normal = torch.distributions.Normal(loc=0, scale=1)

normal.sample(X.shape)
tensor([[ 1.84],
        [ 0.52],
        [-1.71],
        [-1.70],
        [-0.13],
        [-0.60],
        [ 0.14],
        [-0.15],
        [ 2.61],
        [-0.43],
        [ 0.35],
        [-0.06],
        [ 1.48],
        [ 0.49],
        [ 0.25],
        [ 1.75],
        [ 0.74],
        [ 0.03],
        [-1.17],
        [-1.51]])

        最后,可以使用下面的代码计算Y

Y = (1.5*X + 2) + normal.sample(X.shape)

Y
tensor([[15.00],
        [15.00],
        [-0.36],
        [ 6.75],
        [13.59],
        [15.16],
        [ 2.33],
        [ 8.72],
        [ 2.67],
        [ 1.81],
        [13.74],
        [14.06],
        [ 7.15],
        [12.81],
        [15.91],
        [13.15],
        [ 6.76],
        [18.05],
        [18.71],
        [ 6.80]])

它们也可以与 matplotlib 一起绘制,以便更好地理解它们的关系:

import matplotlib.pyplot as plt

plt.scatter(X,Y)
plt.xlim(-1,11)
plt.ylim(0,20)
plt.xlabel("$X$")
plt.ylabel("$Y$")
plt.show()

 

        虽然为示例生成数据似乎违反直觉,但它是演示回归如何工作的好方法。该模型(如下所示)将仅提供 X 和 Y,并且需要将 w₁ 标识为 1.5,将 w₀ 标识为 2。

        

        权重可以存储在数组 w 中。 这个数组中有两个权重,一个用于偏差,一个用于特征的数量。它的形状为 (数字特征 + 1 个偏差,1)。对于此示例,数组的形状为 (2, 1)。

torch.manual_seed(5)
w = torch.rand(size=(2, 1))
w 
tensor([[0.83],
        [0.13]])

        生成这些值后,可以创建模型。

3.2 创建模型

        模型的第一步是为最佳拟合线定义一个函数,为 MSE 定义另一个函数。

        如前所述,该模型的方程为 Ŷ = Xw₁+ w₀。 截至目前,偏差已添加到每个样本中。这相当于将偏差广播为与 X 大小相同并将数组相加。输出如下所示。

w[1]*X + w[0]
tensor([[1.97],
        [2.09],
        [0.83],
        [1.21],
        [1.84],
        [1.84],
        [0.83],
        [1.33],
        [0.96],
        [0.83],
        [1.71],
        [1.97],
        [1.21],
        [1.71],
        [1.97],
        [1.71],
        [1.21],
        [2.09],
        [2.09],
        [1.33]])

        下面的函数计算输出。

# line of best fit
def model(w, X):
  """
    Inputs:
      w: array of weights | (num features + 1 bias, 1)
      X: array of inputs  | (n samples, num features + 1 bias)

    Output:
      returns the predictions | (n samples, 1)
  """

  return w[1]*X + w[0]

        MSE 的功能非常简单:

# mean squared error (MSE)
def MSE(Yhat, Y):
  """
    Inputs:
      Yhat: array of predictions | (n samples, 1)
      Y: array of expected outputs | (n samples, 1)
    Output:
      returns the loss of the model, which is a scalar
  """

  return torch.mean((Yhat-Y)**2) # mean((error)^2)

3.3 预览最佳拟合线

        创建函数后,可以使用绘图预览最佳拟合线,并且可以创建标准函数以供将来使用。它将以红色显示最佳拟合线,以橙色显示每个输入的预测,以蓝色显示预期输出。

def plot_lbf():
  """
    Output:
      plots the line of best fit in comparison to the training data
  """

  # plot the points
  plt.scatter(X,Y)

  # predictions for the line of best fit
  Yhat = model(w, X)
  plt.scatter(X, Yhat, zorder=3) # plot the predictions

  # plot the line of best fit
  X_plot = torch.arange(-1,11+0.1,.1) # generate values with a step of .1
  plt.plot(X_plot, model(w, X_plot), color="red", zorder=0)

  plt.xlim(-1, 11)
  plt.xlabel("$X$")
  plt.ylabel("$Y$")
  plt.title(f"MSE: {MSE(Yhat, Y):.2f}")
  plt.show()

plot_lbf()

 

        具有当前权重的输出并不理想,因为MSE为105.29。为了获得更好的MSE,需要选择不同的权重。它们可以再次随机化,但获得完美线的机会很小。这是梯度下降算法可用于以定义的方式更改权重值的地方。

3.4 梯度下降

        梯度下降算法的解释可以在这里找到:梯度下降的简单介绍。在继续之前应阅读本文以避免混淆。

        总结一下这篇文章,梯度下降使用成本函数的梯度来揭示每个权重对其的方向和影响。通过使用学习率缩放梯度并从每个权重的当前值中减去梯度,成本函数最小化,迫使模型的预测尽可能接近预期输出。

对于简单的线性回归,f 将是 MSE。Python 实现可以在下面看到。请记住,每个权重都有自己的偏导数用于公式,如上所示。

# optimizer
def gradient_descent(w):
  """
    Inputs:
      w: array of weights | (num features + 1 bias, 1)

    Global Variables / Constants:
      X: array of inputs  | (n samples, num features + 1 bias)
      Y: array of expected outputs | (n samples, 1)
      lr: learning rate to scale the gradient

    Output:
      returns the updated weights
  """ 

  n = len(X)

  # update the bias
  w[0] = w[0] - lr*2/n * torch.sum(model(w,X) - Y)
  
  # update the weight
  w[1] = w[1] - lr*2/n * torch.sum(X*(model(w,X) - Y))

  return w

        现在,该函数可用于更新权重。学习率是根据经验选择的,但它通常是一个较小的值。还可以绘制最佳拟合的新线。

lr = 0.01

print("weights before:", w.flatten())
print("MSE before:", MSE(model(w,X), Y))

# update the weights
w = gradient_descent(w)

print("weights after:", w.flatten())
print("MSE after:", MSE(model(w,X), Y))

plot_lbf()
weights before: tensor([0.83, 0.13])
MSE before: tensor(105.29)
weights after: tensor([1.01, 1.46])
MSE after: tensor(2.99)

 

        MSE 在第一次尝试时下降了 100 多分,但这条线仍然不能完全符合点数。请记住,目标是将 w₀ 设为 2,将 w₁ 设为 1.5。为了加快学习过程,可以再执行500次梯度下降,并且可以检查新结果。

# update the weights
for i in range(0, 500):
  # update the weights
  w = gradient_descent(w)

  # print the new values every 10 iterations
  if (i+1) % 100 == 0:
    print("epoch:", i+1)
    print("weights:", w.flatten())
    print("MSE:", MSE(model(w,X), Y))
    print("="*10)

plot_lbf()
epoch: 100
weights: tensor([1.44, 1.59])
MSE: tensor(1.31)
==========
epoch: 200
weights: tensor([1.67, 1.56])
MSE: tensor(1.25)
==========
epoch: 300
weights: tensor([1.80, 1.54])
MSE: tensor(1.24)
==========
epoch: 400
weights: tensor([1.87, 1.53])
MSE: tensor(1.23)
==========
epoch: 500
weights: tensor([1.91, 1.52])
MSE: tensor(1.23)
==========

 

        500 个时期后,MSE 为 1.23。w₀ 为 1.91,w₁ 为 1.52。这意味着模型成功识别了最佳拟合线。可以执行其他更新,但添加到输出值的随机性可能会阻止模型实现完美的预测。

        为了建立关于梯度下降如何工作的额外直觉,可以通过将它们与其输出 MSE 绘制来检查 w₀ 和 w₁ 的影响。绘制梯度下降的函数可以在附录中检查,输出可以在下面检查:

torch.manual_seed(5)
w = torch.rand(size=(2, 1))

w0s, w1s, losses = list(),list(),list()

# update the weights
for i in range(0, 500):
  if i == 0 or (i+1) % 10 == 0:
    w0s.append(float(w[0]))
    w1s.append(float(w[1]))
    losses.append(MSE(model(w,X), Y))

  # update the weights
  w = gradient_descent(w)

plot_GD([-2, 5.2], [-2, 5.2])

 

        每个橙色点表示权重的更新,红线表示从一个迭代到下一个迭代的变化。最大的更新是从第一次迭代到第二次迭代,即红线。其他橙色点靠得很近,因为它们的衍生物很小,因此更新更小。该图显示了权重如何更新,直到获得最佳 MSE。

        虽然这种方法很有用,但可以通过几种方式简化。首先,它没有利用矩阵乘法,这将简化模型的方程。其次,梯度下降不是回归的闭式解,因为每个问题的 epoch 数和学习率都不同,并且解是近似的。本文的最后一部分将解决第一个问题,下一篇文章将解决第二个问题。

四、另一种方法

        虽然这种方法很有用,但它并不像它可能的那么简单。它不利用矩阵。截至目前,整个方程 Ŷ = Xw₁+ w₀ 用于模型的函数,并且必须单独计算每个权重的偏导数以进行梯度下降。通过使用矩阵运算和微积分,这两个函数都简化了。

        首先,的形状为 (n 个样本,num 特征),的形状为 (num 特征 + 1 个偏差,1)。通过在 X 中添加额外的列,可以使用矩阵乘法,因为它将具有 (n 个样本、num 特征 + 1 个偏差)的新形状。这可以是一列将乘以偏差的列,这将缩放向量。这相当于广播偏差,这是先前计算预测的方式。

X = torch.hstack((torch.ones(X.shape),X))
X
tensor([[ 1.,  9.],
        [ 1., 10.],
        [ 1.,  0.],
        [ 1.,  3.],
        [ 1.,  8.],
        [ 1.,  8.],
        [ 1.,  0.],
        [ 1.,  4.],
        [ 1.,  1.],
        [ 1.,  0.],
        [ 1.,  7.],
        [ 1.,  9.],
        [ 1.,  3.],
        [ 1.,  7.],
        [ 1.,  9.],
        [ 1.,  7.],
        [ 1.,  3.],
        [ 1., 10.],
        [ 1., 10.],
        [ 1.,  4.]])

        这会将等式更改为 Ŷ = X₁w₁+ X₀w₀。展望未来,偏差可以被视为一个特征,因此 num 特征可以表示自变量和偏差,并且可以省略 + 1 偏差。因此,X 的大小为 (n 个样本,num 特征),w 的大小为 (num features1)。当它们相互相乘时,输出是预测向量,其大小为 (n 个样本,1)。矩阵乘法的输出与 相同。w[1]*X + w[0]

torch.manual_seed(5)
w = torch.rand(size=(2, 1))

torch.matmul(X, w)
tensor([[1.97],
        [2.09],
        [0.83],
        [1.21],
        [1.84],
        [1.84],
        [0.83],
        [1.33],
        [0.96],
        [0.83],
        [1.71],
        [1.97],
        [1.21],
        [1.71],
        [1.97],
        [1.71],
        [1.21],
        [2.09],
        [2.09],
        [1.33]])

考虑到这一点,可以更新模型的功能:

# line of best fit
def model(w, X):
  """
    Inputs:
      w: array of weights | (num features, 1)
      X: array of inputs  | (n samples, num features)

    Output:
      returns the output of X@w | (n samples, 1)
  """

  return torch.matmul(X, w)

        由于不再将每个权重视为单个分量,因此梯度下降算法也可以更新。基于梯度下降的简单介绍,矩阵的梯度下降算法如下:

        这可以通过 PyTorch 轻松实现。由于 w 在文章开头被重塑,因此导数的输出需要重新整形以进行减法。

# optimizer
def gradient_descent(w):
  """
    Inputs:
      w: array of weights | (num features, 1)

    Global Variables / Constants:
      X: array of inputs  | (n samples, num features)
      Y: array of expected outputs | (n samples, 1)
      lr: learning rate to scale the gradient

    Output:
      returns the updated weights | (num features, 1)
  """ 

  n = X.shape[0]

  return w - (lr * 2/n) * (torch.matmul(-Y.T, X) + torch.matmul(torch.matmul(w.T, X.T), X)).reshape(w.shape)

        使用 500 个 epoch,可以生成与以前相同的输出:

lr = 0.01

# update the weights
for i in range(0, 501):
  # update the weights
  w = gradient_descent(w)

  # print the new values every 10 iterations
  if (i+1) % 100 == 0:
    print("epoch:", i+1)
    print("weights:", w.flatten())
    print("MSE:", MSE(model(w,X), Y))
    print("="*10)
epoch: 100
weights: tensor([1.43, 1.59])
MSE: tensor(1.31)
==========
epoch: 200
weights: tensor([1.66, 1.56])
MSE: tensor(1.25)
==========
epoch: 300
weights: tensor([1.79, 1.54])
MSE: tensor(1.24)
==========
epoch: 400
weights: tensor([1.87, 1.53])
MSE: tensor(1.23)
==========
epoch: 500
weights: tensor([1.91, 1.53])
MSE: tensor(1.23)
==========

        由于这些函数不需要为每个要素手动添加其他变量,因此可用于多元线性回归和多项式回归。

五、结论

        下一篇文章将讨论不近似权重的回归闭式解决方案。相反,最小化的值将使用 Python 中的机器学习简介:Python 中回归的正态方程直接计算。

请不要忘记点赞和关注!:)

七、引用

  1. plot 3D 绘图

六、附录

绘制梯度下降

此函数利用 Plotly 在三维空间中显示梯度下降。

import plotly.graph_objects as go
import plotly
import plotly.express as px

def plot_GD(w0_range, w1_range):
  """
    Inputs:
      w0_range: weight range [w0_low, w0_high]
      w1_range: weight range [w1_low, w1_high]

    Global Variables:
      X: array of inputs  | (n samples, num features + 1 bias)
      Y: array of expected outputs | (n samples, 1)
      lr: learning rate to scale the gradient
      
    Output:
      prints gradient descent
  """ 

  # generate all the possible weight combinations (w0, w1)
  w0_plot, w1_plot = torch.meshgrid(torch.arange(w0_range[0],
                                                 w0_range[1],
                                                 0.1),
                                    torch.arange(w1_range[0],
                                                 w1_range[1],
                                                 0.1))
                                 
  # rearrange into coordinate pairs
  w_plot = torch.hstack((w0_plot.reshape(-1,1), w1_plot.reshape(-1,1)))

  # calculate the MSE for each pair
  mse_plot = [MSE(model(w, X), Y) for w in w_plot]

  # plot the data
  fig = go.Figure(data=[go.Mesh3d(x=w_plot[:,0], 
                                  y=w_plot[:,1],
                                  z=mse_plot,)])

  # plot gradient descent on loss function
  fig.add_scatter3d(x=w0s, 
                    y=w1s, 
                    z=losses, 
                    marker=dict(size=3,color="orange"),
                    line=dict(color="red",width=5))
  
  # prepare ranges for plotting
  xaxis_range = [w0 + 0.01 if w0 < 0 else w0 - 0.01 for w0 in w0_range] 

  yaxis_range = [w1 + 0.01 if w1 < 0 else w1 - 0.01 for w1 in w1_range] 

  fig.update_layout(scene = dict(xaxis_title='w<sub>0</sub>', 
                                 yaxis_title='w<sub>1</sub>', 
                                 zaxis_title='MSE',
                                 xaxis_range=xaxis_range,
                                 yaxis_range=yaxis_range))
  fig.show()

 七、参考和引用

  1. plot 3D 绘图
  2. 亨特·菲利普斯

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

下载的文件被Windows 11 安全中心自动删除

今天从CSDN上下载了自己曾经上传的文件&#xff0c;但是浏览器下载完之后文件被Windows安全中心自动删除&#xff0c;说是带病毒。实际是没有病毒的&#xff0c;再说了即便有病毒也不应该直接删除啊&#xff0c;至少给用户一个保留或删除的选项。 研究了一番&#xff0c;可以暂…

springboot整合第三方技术邮件系统

springboot整合第三方技术邮件系统&#xff0c;发邮件是java程序的基本操作&#xff0c;springboot整合javamail其实就是简化开发。不熟悉邮件的小伙伴可以先学习完javamail的基础操作&#xff0c;再来看这一部分内容才能感触到springboot整合javamail究竟简化了哪些操作。简化…

vue ui 创建项目没有反应

问题 cmd中输入 vue ui 没有反应 解决办法 vue ui命令需要vue3.0以上的版本才可以 1、查看当前版本 vue --version vue版本在3.0以下是没有ui命令的 2、查看版本所拥有的命令 vue -h 3、卸载之前版本的vue npm uninstall vue-cli -g 卸载完成&#xff0c;检查是否已经…

elementui table 在浏览器分辨率变化的时候界面异常

异常点&#xff1a; 界面显示不完整&#xff0c;表格卡顿&#xff0c;界面已经刷新完成&#xff0c;但是表格的宽度还在一点一点变化&#xff0c;甚至有无线延伸的情况 思路&#xff1a; 1. 使用doLayout 这里官方文档有说明&#xff0c; 所以我的想法是&#xff0c;监听浏览…

HTML5-1-标签及属性

文章目录 语法规范标签规范标签列表通用属性基本布局 页面的组成&#xff1a; HTML&#xff08;HyperText Markup Language&#xff0c;超文本标记语言&#xff09;是用来描述网页的一种语言&#xff0c;它不是一种编程语言&#xff0c;而是一种标记语言。 HTML5 是下一代 HTM…

第四章:树形结构的关联式容器(map+set)

系列文章目录 文章目录 系列文章目录前言1、关联式容器与序列式容器1.1 键值对 2、set的介绍3、multiset的介绍3.1 接口count与容器multiset 4、map的介绍4.1 接口insert4.2 operator[]和at 5、multimap的介绍 前言 根据应用场景的不桶&#xff0c;STL总共实现了两种不同结构的…

Elasticsearch(十四)搜索---搜索匹配功能⑤--全文搜索

一、前言 不同于之前的term。terms等结构化查询&#xff0c;全文搜索首先对查询词进行分析&#xff0c;然后根据查询词的分词结果构建查询。这里所说的全文指的是文本类型数据&#xff08;text类型&#xff09;,默认的数据形式是人类的自然语言&#xff0c;如对话内容、图书名…

springboot+mp完成简单案例

目录 1.框架搭建 2.前端搭建 3.后端编写 需求&#xff1a;完成简单的连表条件查询以及添加即可 1.框架搭建 1.创建springboot项目 2.相关依赖 <!--web依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boo…

Leetcode每日一题:1448. 统计二叉树中好节点的数目(2023.8.25 C++)

目录 1448. 统计二叉树中好节点的数目 题目描述&#xff1a; 实现代码与解析&#xff1a; dfs 原理思路&#xff1a; 1448. 统计二叉树中好节点的数目 题目描述&#xff1a; 给你一棵根为 root 的二叉树&#xff0c;请你返回二叉树中好节点的数目。 「好节点」X 定义为&…

基于MATLAB开发AUTOSAR软件应用层Code mapping专题-part 4 Data store标签页介绍

这篇文章我们继续讲解code-mapping的Data stores页,这个页的内容对应的SIMULINK中的模块是Data store memory。 我们首先在模型中创建一个Data store memory模块,如图: Data store memory模块的作用相当于一个全局变量,我们可以在模型的功能逻辑里将一个信号存进去,在另…

docker harbor私有库

目录 一.Harbor介绍 二.Harbor的特性 三.Harbor的构成 四.Harbor构建Docker私有仓库 4.2在Server主机上部署Harbor服务&#xff08;192.168.158.25&#xff09; 4.2.1 这时候这边就可以去查看192.168.158.25网页 4.3此时可真机访问serverIP 4.4通过127.0.0.1来登陆和推送镜…

系统架构设计师之缓存技术:Redis与Memcache能力比较

系统架构设计师之缓存技术&#xff1a;Redis与Memcache能力比较

React + Next.js 搭建项目(配有对比介绍一起食用)

文章标题 01 Next.js 是什么02 Next.js 搭建工具 create-next-app03 create-react-app 与 create-next-app 的区别04 快速构建 Next.js 项目05 App Router 与 Pages Router 的区别 01 Next.js 是什么 Next.js 是一个 React 框架&#xff0c;它允许你使用 React 框架建立超强的…

k8s service (三)

K8s service (三) LoadBalancer类型的Service LoadBalancer和NodePort其实是同一种方式&#xff0c;目的都是向外暴露一个端口&#xff0c;区别在于LoadBalancer会在集群的外部再来做一个负载均衡设备&#xff0c;而这个设备需要外部环境支持的&#xff0c;外部服务发送到这…

【安装GPU版本pytorch,torch.cuda.is_available()仍然返回False问题】

TOC 第一步 检查cuda是否安装&#xff0c;CUDA环境变量是否正确设置&#xff0c;比如linux需要设置在PATH&#xff0c;window下环境变量编辑看看&#xff0c;是否有CUDA 第二步&#xff0c;核查python中torch版本 首先查看你环境里的pytorch是否是cuda版本&#xff0c;我这…

EasyExcel自定义字段对象转换器支持转换实体和集合实体

文章目录 1. 实现ObjectConverter2. 使用3. 测试3.2 导出excel3.1 导入excel 1. 实现ObjectConverter package com.tophant.cloud.common.excel.converters;import cn.hutool.json.JSONUtil; import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.…

pdf转ppt软件哪个好用?推荐一个好用的pdf转ppt软件

在日常工作和学习中&#xff0c;我们经常会遇到需要将PDF文件转换为PPT格式的情况。PDF格式的文件通常用于展示和保留文档的原始格式&#xff0c;而PPT格式则更适合用于演示和展示。为了满足这一需求&#xff0c;许多软件提供了PDF转PPT的功能&#xff0c;使我们能够方便地将PD…

最新CMS指纹识别技术

指纹识别 1&#xff0e;CMS简介 CMS&#xff08;Content Management System&#xff0c;内容管理系统&#xff09;&#xff0c;又称整站系统或文章系统&#xff0c;用于网站内容管理。用户只需下载对应的CMS软件包&#xff0c;部署、搭建后就可以直接使用CMS。各CMS具有独特的…

C语言(第三十二天)

1. 递归是什么&#xff1f; 递归是学习C语言函数绕不开的一个话题&#xff0c;那什么是递归呢&#xff1f; 递归其实是一种解决问题的方法&#xff0c;在C语言中&#xff0c;递归就是函数自己调用自己。 写一个史上最简单的C语言递归代码&#xff1a; #include <stdio.h>…

【FPGA】verilog语法的学习与应用 —— 位操作 | 参数化设计

【FPGA】verilog语法的学习与应用 —— 位操作 | 参数化设计 学习新语法&#xff0c;争做新青年 计数器实验升级&#xff0c;让8个LED灯每个0.5s的速率循环闪烁&#xff0c;流水灯ahh好久不见~ 去年光这个就把我折磨够呛。。我肉眼可见的脱发就是从那时候开始的。。在那两个月…