Pytorch100例 | 用深度学习处理分类问题【实战教程】

news2024/11/17 9:32:54


PyTorch和TensorFlow库是用于深度学习的两个最常用的 Python 库。PyTorch 是 Facebook 开发的,而 TensorFlow 是 Google 的项目。在本文中,你将看到如何使用 PyTorch 库来解决分类问题。
分类问题属于机器学习问题的范畴,其中给定一组特征,任务是预测离散值。预测肿瘤是否癌变,或者学生是否可能通过考试,是分类问题的一些常见示例。
在本文中,我们将根据银行客户的某些特征,预测客户是否有可能在 6 个月后离开银行。客户离开的现象也称为客户流失。因此,我们的任务是根据各种客户特征来预测客户流失。
在你继续之前,假定你对 Python 编程语言具有中级水平,并且你已经安装了 PyTorch 库。此外,了解了基本机器学习概念的知识可能会对理解本文有所帮助。如果你尚未安装 PyTorch,则可以使用以下 pip 命令进行安装:

$ pip install pytorch

数据集

我们将在本文中使用的数据集可通过此Kaggle 链接免费获得,也可以添加我微信获取。让我们将所需的库和数据集导入我们的 Python 应用程序:

import torch
import torch.nn as nn
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

我们可以使用第三方库的read_csv()方法pandas导入包含我们数据集的 CSV 文件。

dataset = pd.read_csv(r'E:Datasets\customer_data.csv')

让我们打印数据集的形状:

dataset.shape

输出:

(10000, 14)

输出显示数据集有 10,000 条记录和 14 列。
我们可以使用.head()方法打印数据集的前五行。

dataset.head()

输出:

你可以在我们的数据集中看到 14 列。我们的任务是基于前 13 列预测第 14 列的值,即Exited。需要注意的是,前 13 列的值是在Exited获取该列的值之前 6 个月记录的,因为任务是预测自记录客户信息起 6 个月后的客户流失。

探索性数据分析

让我们对我们的数据集执行一些探索性数据分析。我们将首先预测 6 个月后实际离开银行的客户的比例,并使用饼图进行可视化。
让我们首先增加图表的默认绘图大小:

fig_size = plt.rcParams["figure.figsize"]
fig_size[0] = 10
fig_size[1] = 8
plt.rcParams["figure.figsize"] = fig_size

以下脚本绘制Exited列的饼图。

dataset.Exited.value_counts().plot(kind='pie', autopct='%1.0f%%', colors=['skyblue', 'orange'], explode=(0.05, 0.05))

输出:

输出显示,在我们的数据集中,20% 的客户离开了银行。这里1代表客户离开银行的情况,0代表客户没有离开银行的情况。
让我们绘制数据集中所有地理位置的客户数量:

sns.countplot(x='Geography', data=dataset)

输出:

输出显示,几乎一半的客户属于法国,而属于西班牙和德国的客户比例各为 25%。
现在让我们绘制每个独特地理位置的客户数量以及客户流失信息。我们可以使用seaborn库中的countplot()函数来做到这一点。

sns.countplot(x='Exited', hue='Geography', data=dataset)

输出:

输出显示,尽管法国客户的总数是西班牙和德国客户的两倍,但法国和德国客户离开银行的客户比例相同。同样,德国和西班牙客户的总体数量相同,但德国客户离开银行的数量是西班牙客户的两倍,这表明德国客户更有可能在 6 个月后离开银行。

数据预处理

在我们训练我们的 PyTorch 模型之前,我们需要预处理我们的数据。如果查看数据集,你会看到它有两种类型的列:数值列和分类列。数字列包含数字信息。CreditScoreBalanceAge等。类似地,GeographyGender是分类列,因为它们包含分类信息,例如客户的位置和性别。有一些列可以被视为数字列和分类列。例如,该HasCrCard列的值可以是 1 或 0。但是,那HasCrCard列包含有关客户是否拥有信用卡的信息。建议将既可被视为分类又可被视为数值的列视为分类。
让我们再次打印数据集中的所有列,并找出哪些列可以被视为数字列,哪些列应该被视为分类列。数据框的columns属性打印所有列名:

dataset.columns

输出:

Index(['RowNumber', 'CustomerId', 'Surname', 'CreditScore', 'Geography',
       'Gender', 'Age', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard',
       'IsActiveMember', 'EstimatedSalary', 'Exited'],
      dtype='object')

在我们数据集中的列中,我们不会使用RowNumberCustomerIdSurname列,因为这些列的值是完全随机的并且与我们需要预测的目标无关。例如,客户的姓氏对客户是否会离开银行没有影响。在其余列中,GeographyGenderHasCrCardIsActiveMember列可以视为分类列。让我们创建这些列的列表:

categorical_columns = ['Geography', 'Gender', 'HasCrCard', 'IsActiveMember']

Exited列外的所有剩余列都可以视为数字列。

numerical_columns = ['CreditScore', 'Age', 'Tenure', 'Balance', 'NumOfProducts', 'EstimatedSalary']

最后,输出(Exited列)存储在outputs变量中。

outputs = ['Exited']

我们已经创建了分类、数字和输出的列表。但是,目前分类列的类型不是分类的。你可以使用以下脚本检查数据集中所有列的类型:

dataset.dtypes

输出:

RowNumber            int64
CustomerId           int64
Surname             object
CreditScore          int64
Geography           object
Gender              object
Age                  int64
Tenure               int64
Balance            float64
NumOfProducts        int64
HasCrCard            int64
IsActiveMember       int64
EstimatedSalary    float64
Exited               int64
dtype: object

你可以看到GeographyGender列的类型为object,HasCrCard 和IsActive 列的类型为int64,我们需要将分类列的类型转换为category,我们可以使用函数来做到这一点,如下所示:

for category in categorical_columns:
    dataset[category] = dataset[category].astype('category')

现在,如果您再次绘制数据集中列的类型,您应该会看到以下结果:

dataset.dtypes

输出

RowNumber             int64
CustomerId            int64
Surname              object
CreditScore           int64
Geography          category
Gender             category
Age                   int64
Tenure                int64
Balance             float64
NumOfProducts         int64
HasCrCard          category
IsActiveMember     category
EstimatedSalary     float64
Exited                int64
dtype: object

现在让我们看看Geography列中的所有类别:

dataset['Geography'].cat.categories

输出:

Index(['France', 'Germany', 'Spain'], dtype='object')

当你将列的数据类型更改为类别时,列中的每个类别都会分配一个唯一代码。例如,让我们绘制该Geography列的前五行并打印前五行的代码值:

dataset['Geography'].head()

输出:

0    France
1     Spain
2    France
3    France
4     Spain
Name: Geography, dtype: category
Categories (3, object): [France, Germany, Spain]

以下脚本绘制Geography列前五行中值的编号:

dataset['Geography'].head().cat.codes

输出:

0    0
1    2
2    0
3    0
4    2
dtype: int8

输出显示法国已编码为 0,西班牙已编码为 2。
由于我们将使用 PyTorch 进行模型训练,因此我们需要将分类列和数值列转换为张量。让我们首先将分类列转换为张量。在 PyTorch 中,可以通过 numpy 数组创建张量。我们首先将四个分类列中的数据转换为 numpy 数组,然后水平堆叠所有列,如以下脚本所示:

geo = dataset['Geography'].cat.codes.values
gen = dataset['Gender'].cat.codes.values
hcc = dataset['HasCrCard'].cat.codes.values
iam = dataset['IsActiveMember'].cat.codes.values

categorical_data = np.stack([geo, gen, hcc, iam], 1)

categorical_data[:10]

上面的脚本打印分类列中水平堆叠的前十条记录。输出如下:
输出:

array([[0, 0, 1, 1],
       [2, 0, 0, 1],
       [0, 0, 1, 0],
       [0, 0, 0, 0],
       [2, 0, 1, 1],
       [2, 1, 1, 0],
       [0, 1, 1, 1],
       [1, 0, 1, 0],
       [0, 1, 0, 1],
       [0, 1, 1, 1]], dtype=int8)

现在要从前面提到的 numpy 数组创建一个张量,你可以简单地将数组传递给模块的torch类的tensor模块。对于分类列,数据类型应为torch.int64.

categorical_data = torch.tensor(categorical_data, dtype=torch.int64)
categorical_data[:10]

输出:

tensor([[0, 0, 1, 1],
        [2, 0, 0, 1],
        [0, 0, 1, 0],
        [0, 0, 0, 0],
        [2, 0, 1, 1],
        [2, 1, 1, 0],
        [0, 1, 1, 1],
        [1, 0, 1, 0],
        [0, 1, 0, 1],
        [0, 1, 1, 1]])

在输出中,你可以看到分类数据的 numpy 数组现已转换为tensor对象。以同样的方式,我们可以将数值列转换为张量:

numerical_data = np.stack([dataset[col].values for col in numerical_columns], 1)
numerical_data = torch.tensor(numerical_data, dtype=torch.float)
numerical_data[:5]

输出:

tensor([[6.1900e+02, 4.2000e+01, 2.0000e+00, 0.0000e+00, 1.0000e+00, 1.0135e+05],
        [6.0800e+02, 4.1000e+01, 1.0000e+00, 8.3808e+04, 1.0000e+00, 1.1254e+05],
        [5.0200e+02, 4.2000e+01, 8.0000e+00, 1.5966e+05, 3.0000e+00, 1.1393e+05],
        [6.9900e+02, 3.9000e+01, 1.0000e+00, 0.0000e+00, 2.0000e+00, 9.3827e+04],
        [8.5000e+02, 4.3000e+01, 2.0000e+00, 1.2551e+05, 1.0000e+00, 7.9084e+04]])

在输出中,你可以看到前五行包含我们数据集中六个数字列的值。
最后一步是将输出的 numpy 数组转换为tensor对象。

outputs = torch.tensor(dataset[outputs].values).flatten()
outputs[:5]

输出:

tensor([1, 0, 1, 0, 0])

现在让我们绘制分类数据、数值数据和相应输出的形状:

print(categorical_data.shape)
print(numerical_data.shape)
print(outputs.shape)

输出:

torch.Size([10000, 4])
torch.Size([10000, 6])
torch.Size([10000])

在我们训练模型之前有一个非常重要的步骤。我们将分类列转换为数字列(类别数字化),其中唯一值由整数表示。例如,在Geography列中,我们看到法国用 0 表示,德国用 1 表示。我们可以使用这些值来训练我们的模型。但是,更好的方法是以 N 维向量的形式表示分类列中的值,而不是整数。向量能够捕获更多信息,并能以更合适的方式找到不同分类值之间的关系。因此,我们将以 N 维向量的形式表示分类列中的值。这个过程称为嵌入。
我们需要为所有分类列定义嵌入大小(向量维度)。关于维数没有硬性规定。定义列的嵌入大小的一个好的经验法则是将列中唯一值的数量除以 2(但不超过 50)。例如,对于该Geography列,唯一值的数量为 3。该Geography列的相应嵌入大小将为 3/2 = 1.5 = 2(四舍五入)。
以下脚本创建一个元组,其中包含唯一值的数量和所有分类列的维度大小:

categorical_column_sizes = [len(dataset[column].cat.categories) for column in categorical_columns]
categorical_embedding_sizes = [(col_size, min(50, (col_size+1)//2)) for col_size in categorical_column_sizes]
print(categorical_embedding_sizes)

输出:

[(3, 2), (2, 1), (2, 1), (2, 1)]

对于有监督的深度学习模型,例如我们在本文中开发的模型,需要使用训练数据进行训练,并在测试数据集上评估模型性能。因此,我们需要将我们的数据集划分为训练集和测试集,如下面的脚本所示:

total_records = 10000
test_records  = int(total_records * .2)

categorical_train_data = categorical_data[:total_records-test_records]
categorical_test_data = categorical_data[total_records-test_records:total_records]
numerical_train_data = numerical_data[:total_records-test_records]
numerical_test_data  = numerical_data[total_records-test_records:total_records]
train_outputs = outputs[:total_records-test_records]
test_outputs  = outputs[total_records-test_records:total_records]

我们的数据集中有 10,000 条记录,其中 80% 的记录,即 8000 条记录,将用于训练模型,而其余 20% 的记录将用于评估我们模型的性能。请注意,在上面的脚本中,分类和数值数据以及输出已分为训练集和测试集。
为了验证我们是否正确地将数据划分为训练集和测试集,让我们打印训练和测试记录的长度:

print(len(categorical_train_data))
print(len(numerical_train_data))
print(len(train_outputs))

print(len(categorical_test_data))
print(len(numerical_test_data))
print(len(test_outputs))

输出:

8000
8000
8000
2000
2000
2000

创建预测模型

我们已将数据分为训练集和测试集,现在是时候定义我们的训练模型了。为此,我们可以定义一个名为Model的类,它将用于训练模型。

class Model(nn.Module):

    def __init__(self, embedding_size, num_numerical_cols, output_size, layers, p=0.4):
        super().__init__()
        self.all_embeddings = nn.ModuleList([nn.Embedding(ni, nf) for ni, nf in embedding_size])
        self.embedding_dropout = nn.Dropout(p)
        self.batch_norm_num = nn.BatchNorm1d(num_numerical_cols)

        all_layers = []
        num_categorical_cols = sum((nf for ni, nf in embedding_size))
        input_size = num_categorical_cols + num_numerical_cols

        for i in layers:
            all_layers.append(nn.Linear(input_size, i))
            all_layers.append(nn.ReLU(inplace=True))
            all_layers.append(nn.BatchNorm1d(i))
            all_layers.append(nn.Dropout(p))
            input_size = i

        all_layers.append(nn.Linear(layers[-1], output_size))

        self.layers = nn.Sequential(*all_layers)

    def forward(self, x_categorical, x_numerical):
        embeddings = []
        for i,e in enumerate(self.all_embeddings):
            embeddings.append(e(x_categorical[:,i]))
        x = torch.cat(embeddings, 1)
        x = self.embedding_dropout(x)

        x_numerical = self.batch_norm_num(x_numerical)
        x = torch.cat([x, x_numerical], 1)
        x = self.layers(x)
        return x

如果你以前从未使用过 PyTorch,上面的代码可能看起来会有些难度,我尽力为你分解。
在第一行中,我们声明了一个Model继承自Module的类。在类的构造函数(__init__()方法)中传递以下参数:

  1. embedding_size:包含分类列的嵌入大小(embedding size)
  2. num_numerical_cols:存储数字列的总数
  3. output_size:输出层的大小或可能输出的数量。
  4. layers:包含所有层的神经元数量的列表。
  5. p:Dropout值,默认值为0.5

在构造函数内部,初始化了一些变量。首先,该all_embeddings变量包含所有分类列的ModuleList对象列表。embedding_dropout存储所有层的丢失值。最后,batch_norm_num存储所有数字列的BatchNorm1d对象列表。
接下来,为了找到输入层的大小,将分类列数和数字列数相加并存储在input_size变量中。之后,for循环迭代并将相应的层添加到all_layers列表中。添加的层是:

  • Linear:用于计算输入和权重矩阵之间的点积
  • ReLu:用作激活函数
  • BatchNorm1d: 用于对数值列应用批量归一化
  • Dropout:用于避免过拟合

for循环后,输出层将附加到层列表。由于我们希望神经网络中的所有层按顺序执行,因此将层列表传递给nn.Sequential类。
接下来,在该forward方法中,分类列和数字列都作为输入传递。分类列的嵌入发生在以下几行中。

embeddings = []
for i, e in enumerate(self.all_embeddings):
    embeddings.append(e(x_categorical[:,i]))
x = torch.cat(embeddings, 1)
x = self.embedding_dropout(x)

数字列的批量归一化应用以下脚本:

x_numerical = self.batch_norm_num(x_numerical)

最后,嵌入的分类列x和数字列x_numerical连接在一起并传递给 sequential layers

训练模型

要训练模型,首先我们必须创建一个Model(我们在上一节中定义的类的对象)。

model = Model(categorical_embedding_sizes, numerical_data.shape[1], 2, [200,100,50], p=0.4)

你可以看到我们传递了分类列的嵌入大小、数字列的数量、输出大小(在我们的例子中为 2)和隐藏层中的神经元。你可以看到我们有三个隐藏层,分别有 200、100 和 50 个神经元。如果需要,你可以选择任何其他尺寸。
让我们打印我们的模型,看看它的外观:

print(model)

输出:

Model(
  (all_embeddings): ModuleList(
    (0): Embedding(3, 2)
    (1): Embedding(2, 1)
    (2): Embedding(2, 1)
    (3): Embedding(2, 1)
  )
  (embedding_dropout): Dropout(p=0.4)
  (batch_norm_num): BatchNorm1d(6, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (layers): Sequential(
    (0): Linear(in_features=11, out_features=200, bias=True)
    (1): ReLU(inplace)
    (2): BatchNorm1d(200, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (3): Dropout(p=0.4)
    (4): Linear(in_features=200, out_features=100, bias=True)
    (5): ReLU(inplace)
    (6): BatchNorm1d(100, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (7): Dropout(p=0.4)
    (8): Linear(in_features=100, out_features=50, bias=True)
    (9): ReLU(inplace)
    (10): BatchNorm1d(50, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (11): Dropout(p=0.4)
    (12): Linear(in_features=50, out_features=2, bias=True)
  )
)

你可以看到,在第一个线性层中,in_features变量的值为 11,因为我们有 6 个数字列,并且分类列的嵌入维度之和为 5,因此 6+5 = 11。类似地,在最后一层中,out_features值为 2,因为我们只有 2 个可能的输出。
在我们真正训练我们的模型之前,我们需要定义损失函数和将用于训练模型的优化器。由于我们正在解决分类问题,因此我们将使用交叉熵损失。对于优化器功能,我们将使用adam 优化器。
以下脚本定义了损失函数和优化器:

loss_function = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

现在我们拥有训练模型所需的一切。以下脚本训练模型:

epochs = 300
aggregated_losses = []

for i in range(epochs):
    i += 1
    y_pred = model(categorical_train_data, numerical_train_data)
    single_loss = loss_function(y_pred, train_outputs)
    aggregated_losses.append(single_loss)

    if i%25 == 1:
        print(f'epoch: {i:3} loss: {single_loss.item():10.8f}')

    optimizer.zero_grad()
    single_loss.backward()
    optimizer.step()

print(f'epoch: {i:3} loss: {single_loss.item():10.10f}')

epochs 的数量设置为 300,这意味着要训练模型,完整的数据集将被使用 300 次。for循环执行300 次,在每次迭代期间,使用损失函数计算损失。每次迭代期间的损失都附加到aggregated_loss列表中。调用single_loss对象的backward()函数更新权重。最后,optimizer函数的step()方法更新梯度。每 25 个epoch后打印一次损失。
上面脚本的输出如下:

epoch:   1 loss: 0.71847951
epoch:  26 loss: 0.57145703
epoch:  51 loss: 0.48110831
epoch:  76 loss: 0.42529839
epoch: 101 loss: 0.39972275
epoch: 126 loss: 0.37837571
epoch: 151 loss: 0.37133673
epoch: 176 loss: 0.36773482
epoch: 201 loss: 0.36305946
epoch: 226 loss: 0.36079505
epoch: 251 loss: 0.35350436
epoch: 276 loss: 0.35540250
epoch: 300 loss: 0.3465710580

以下脚本绘制了针对历元的损失:

plt.plot(range(epochs), aggregated_losses)
plt.ylabel('Loss')
plt.xlabel('epoch');

输出:

输出显示最初损失迅速减少。在大约第 250 个epoch之后,损失几乎没有减少。

做出预测

最后一步是对测试数据进行预测。为此,我们只需要将categorical_test_data和传递numerical_test_datamodel类。然后可以将返回的值与实际测试输出值进行比较。以下脚本对测试类进行预测并打印测试数据的交叉熵损失。

with torch.no_grad():
    y_val = model(categorical_test_data, numerical_test_data)
    loss = loss_function(y_val, test_outputs)
print(f'Loss: {loss:.8f}')

输出:

Loss: 0.36855841

测试集上的损失为 0.3685,略高于训练集上的 0.3465,这表明我们的模型略微过度拟合。
重要的是要注意,由于我们指定输出层将包含 2 个神经元,因此每个预测将包含 2 个值。例如,前 5 个预测值如下所示:

print(y_val[:5])

输出:

tensor([[ 1.2045, -1.3857],
        [ 1.3911, -1.5957],
        [ 1.2781, -1.3598],
        [ 0.6261, -0.5429],
        [ 2.5430, -1.9991]])

这种预测背后的想法是,如果实际输出为 0,则第一个数应该大于第二个数(索引 0 处的值应高于索引 1 处的值),反之亦然。我们可以使用以下脚本检索列表中最大值的索引:

y_val = np.argmax(y_val, axis=1)

输出:
现在让我们再次打印列表的前五个值y_val

print(y_val[:5])

输出:

tensor([0, 0, 0, 0, 0])

由于在最初预测的输出列表中,对于前五个记录,零索引处的值均大于第一个索引处的值,因此我们可以在处理后的输出的前五行中看到 0。
最后,我们可以使用sklearn.metrics模块中的confusion_matrixaccuracy_scoreclassification_report方法来查找测试集的准确度、精确度和召回值,以及制作混淆矩阵。

from sklearn.metrics import classification_report, confusion_matrix, accuracy_score

print(confusion_matrix(test_outputs,y_val))
print(classification_report(test_outputs,y_val))
print(accuracy_score(test_outputs, y_val))

输出:

[[1527   83]
 [ 224  166]]
              precision    recall  f1-score   support

           0       0.87      0.95      0.91      1610
           1       0.67      0.43      0.52       390

   micro avg       0.85      0.85      0.85      2000
   macro avg       0.77      0.69      0.71      2000
weighted avg       0.83      0.85      0.83      2000

0.8465

输出显示我们的模型达到了 84.65% 的准确率,考虑到我们为神经网络模型随机选择了所有参数这一事实,这是相当令人印象深刻的。我建议尝试更改模型参数,即训练/测试拆分、隐藏层的数量和大小等,看看是否可以获得更好的结果。

结论

PyTorch 是 Facebook 开发的常用深度学习库,可用于分类、回归和聚类等多种任务。本文介绍如何使用 PyTorch 库对表格数据进行分类。

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

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

相关文章

【檀越剑指大厂—SpringBoot】SpringBoot应用

一.配置 1.配置文件 SpringBoot 使用一个全局的配置文件,配置文件名称固定 application.propertiesapplication.yml 配置文件的作用:修改 SpringBoot 自动配置的默认值;SpringBoot 在底层都给我们自动配置好 2.tomcat 配置 server:port: 8081error…

IDEA下使用Git与GitHub【超详细】

IDEA结合Git 初始化Git及提交 查看提交版本 切换版本 创建分支与切换 合并分支 ​编辑 分支冲突 IDEA结合GitHub 创建GitHub账号 上传代码到本地仓库 推送代码 拉取代码 克隆远程库到本地 这里是在学习完Git的基础指令来了解在企业合作开发下如何用集成工具联合Git…

Photoshop - 高反差保留

对图像处理相关学习的一些笔记归档发表,关于锐化的原理; 首先简而言之,当颜色明度为100%的时候,为白色,反之为黑色: 为50%时,就是中性灰; 在混合方式中,变暗这一组&…

JVM之虚拟机栈

1. 虚拟机栈概述 虚拟机栈不存在GC,但存在OOM,程序计数器二者都不存在 2. 栈的存储单位 3. 局部变量表 变量的分类:按照数据类型分:① 基本数据类型 ② 引用数据类型 按照在类中声明的位置分:① 成员变量:在使用前,都经…

【实时数仓】DWM层订单宽表之维表关联异步查询(续)、DWM层支付宽表需求分析、需求实现(源码)

文章目录一 DWM层-订单宽表1 维表关联代码实现(1)优化2:异步查询a 关联省市维度b 关联SKU维度c 关联SPU维度d 关联品类维度e 关联品牌维度f 最终结果展示(2)结果写入kafka sink二 DWM层-支付宽表1 需求分析与思路2 需求…

界面控件DevExpress WinForm v22.1——拥有全新的WXI调色板

DevExpress WinForm拥有180组件和UI库,能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForm能完美构建流畅、美观且易于使用的应用程序,无论是Office风格的界面,还是分析处理大批量的业务数据,它都能轻松胜任…

车载以太网解决方案,你知多少?

近年来,为了满足智能网联汽车的开发要求,车载以太网技术开始逐渐进入人们的视野。而以太网技术已经成为下一代车载络架构的趋势之一,其发展之迅猛,使得各主机厂纷纷产生了浓厚的兴趣并投入研发。 一 为什么使用车载以太网 | 对高…

UE4 GIS Cesium for Unreal插件使用

第一步:安装Cesium for Unreal插件 如果尚未安装,请先安装Cesium for Unreal插件。 在虚幻引擎市场上打开Cesium for Unreal插件页面。2. 登录虚幻引擎商城,并单击免费按钮,将插件安装在虚幻引擎中。 第二步:创建项…

这里有 10 个省时间的 PyCharm 技巧

0. PyCharm 常用快捷键 1. 查看使用库源码 经常听人说,多看源码。源码不仅能帮我们搞清楚运行机制,还能学习优秀的库或者框架的最佳实践。 调用库时,你可以在你好奇的几乎任何地方点击 CommandB,就可以很方便的跳转到源码里的类&…

电商项目6:商品模块-品牌管理

商品模块-品牌管理1、逆向工程生成菜单2、优化逆向生成的前端工程2.1、优化显示状态1、逆向工程生成菜单 将逆向工程生成的两个vue文件放置到前端项目,可以参考电商项目2逆向工程生成 将其两个vue文件复制到product目录下 然后重启前端项目 只有查询,…

【算法】子序列问题合集

前言 动态规划的核心设计思想是数学归纳法 假如我们想证明一个数学结论&#xff1a; 那么先假设这个结论在 k < n 时成立想办法推导证明出 k n 的时候此结论也成立。是需要一个 dp 数组嘛&#xff1f; 可以假设 dp[0...i - 1] 都已经被算出来了然后问自己&#xff1a;怎么…

记录Android Studio连接华为(nova)手机鸿蒙系统踩过的坑

目录 安装Android studio 安装Google USE Driver 查看华为手机的安卓版本 设置开发者模式 安装华为手机助手 重启Android studio 总结 安装Android studio 我主要参考的是这篇文章&#xff1a;Android基础&#xff08;android studio最详细基础使用功略&#xff09;_手下…

14考虑电动汽车可调度潜力的充电站两阶段市场投标策略

参考文献 考虑电动汽车可调度潜力的充电站两阶段市场投标策略——詹祥澎&#xff08;电力系统自动化,2021&#xff09; 主要内容 在电力市场环境下,充电站优化投标策略能降低电力成本&#xff0c;甚至通过售电获取收益。本程序考虑了电动汽车成为柔性储荷资源的潜力&#xf…

【目标检测】只需一张图~YOLOv5原理懂了~

目录 一、简介 二、模型结构 1.整体结构图 2.Backbone&#xff08;CSPDarknet&#xff09; 3.SPPF&#xff08;Spatial Pyramid Pooling - Fast&#xff09; 4.Neck&#xff08;FPNPAN&#xff09; 5.Head 三、anchor编解码 1.anchor编码 2.anchor解码 四、损失函数 …

一文读懂Go函数调用

导读&#xff5c;Go的函数调用时参数是通过栈传递还是寄存器传递&#xff1f;使用哪个版本的Go语言能让程序运行性能提升5%&#xff1f;腾讯后台开发工程师涂明光将带你由浅入深了解函数调用&#xff0c;并结合不同版本Go进行实操解答。函数调用基本概念1&#xff09;调用者cal…

【服务器数据恢复】Linux服务器重装系统后出现空白超级块的数据恢复案例

服务器数据恢复环境&#xff1a; 某品牌X系列服务器&#xff1b; linux操作系统&#xff1b; 4块SAS接口硬盘组建raid5磁盘阵列。 服务器故障&检测&#xff1a; 服务器运行过程中由于未知原因突然瘫痪&#xff0c;用户为故障服务器重新安装操作系统&#xff0c;安装完成后发…

【车载开发系列】UDS诊断---基于事件响应($0x86)

【车载开发系列】UDS诊断—基于事件响应&#xff08;$0x86&#xff09; 诊断---基于事件响应&#xff08;$0x86&#xff09;【车载开发系列】UDS诊断---基于事件响应&#xff08;$0x86&#xff09;一.概念定义二.注意事项三.报文格式1&#xff09;请求报文2&#xff09;肯定响应…

Java+MySQL基于ssm家教服务平台

随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出去&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤&#xff0c;采用SSM框架建设家教服务平台。 本毕业设计主…

11个案例讲透 Python 函数参数

今天给大家分享一下自己整理的一篇 Python 参数的内容&#xff0c;内容非常的干&#xff0c;全文通过案例的形式来理解知识点&#xff0c;自认为比网上 80% 的文章讲的都要明白&#xff0c;如果你是入门不久的 python 新手&#xff0c;相信本篇文章应该对你会有不小的帮助。 接…

第5章 高级SQL

第5章 高级SQL 考试范围&#xff1a; 5.2 -5.3 考试题型&#xff1a; 计算题 考试内容&#xff1a; 函数、过程和触发器的概念 会定义和调用函数、过程 会定义触发器 函数、过程和触发器的概念 函数&&过程 存储过程和函数是一组为了完成特定功能的SQL语句集&…