Image style automatic analysis model

## 1.1 Project background

With the continuous development of computer vision technology, image style analysis has become a hot research field. Picture style analysis can help us better understand the visual elements and artistic style in pictures, so as to find applications in art, design, advertising and other fields. In addition, image style analysis can also be used in recommender systems, image retrieval and other tasks to provide users with more personalized services.

In recent years, deep learning techniques have made remarkable progress in the field of image style analysis. By training a convolutional neural network (CNN) model, we can achieve automatic analysis and classification of image styles. This project aims to use deep learning technology to build a model for automatic analysis of image style.

## 1.2 Project goals

The goal of this project is to use Python and the PyTorch framework to implement an automatic image style analysis model. Specifically, the model should be able to accept a picture as input, determine the style category it belongs to, and give the corresponding confidence. To achieve this goal, we will accomplish the following tasks:

1. Collect and preprocess image style data;
2. Construct image style classification model based on convolutional neural network;
3. Train the model, optimize parameters, and improve model performance;
4. Evaluate the performance of the model on the test set and analyze the performance of the model Advantages and disadvantages;
5. Summarize the project process, discuss possible improvement directions and future work.

Table of contents

## 1.1 Project background

## 1.2 Project goals

#2 Data preparation

## 1. Data Collection

## 2. Data preprocessing

## 3. Data Augmentation

# three model construction

## 1. Choose model architecture

## 2. Define loss function and optimizer

## 3. Implement the training and evaluation process

# Four model training

## 1. Data loading and preprocessing

## 2. Training loop

## 3. Model saving

# five model optimization

## 1. Adjust the model structure

## 2. Data Augmentation

## 3. Regularization

## 4. Learning rate adjustment

## 5. Early Stopping

## 6. Using the pre-trained model (Transfer Learning)

# Six model evaluation

## 1. Accuracy

## 2. Confusion Matrix

## 3. Precision, Recall and F1 Score

## 4. ROC curve and AUC value

## 5. Cross-Validation

7. Summary and Outlook


#2 Data preparation

In this section, we detail the process of data preparation, including data collection, preprocessing, and augmentation.

## 1. Data Collection

In order to train a model for automatic image style analysis, we need a large amount of image data with style labels. Here are some suggested data sources:

1. **WikiArt Dataset**: WikiArt is a dataset covering a variety of artistic styles, containing about 85,000 images, covering 27 major artistic styles. This dataset can be used as the main data source for this project.

2. **Collect data by yourself**: If you need more diverse data, you can try to collect some pictures with copyright permission from the Internet, and manually add style tags to these pictures. You can use a web crawler tool (such as Scrapy) to automatically grab images and metadata.

3. **Merge multiple datasets**: Data can be obtained from other similar datasets, such as Painter by Numbers, etc. Note the need to ensure consistent style labels across datasets.

## 2. Data preprocessing

Before the data is used to train the model, certain preprocessing operations are required.

1. **Resize Image**: Resize all images to the same size. Typically, Convolutional Neural Networks (CNNs) require input images to have the same width and height. You can use OpenCV, PIL and other libraries to uniformly adjust the picture to 224x224 pixels. Sample code:

import cv2

def resize_image(image, new_width, new_height):
    resized_image = cv2.resize(image, (new_width, new_height))
    return resized_image

image = cv2.imread("path/to/image.jpg")
resized_image = resize_image(image, 224, 224)
```

2. **Normalization**: Normalize the pixel value of the picture to between 0-1. This helps to improve the convergence rate of the model. Normalization can be done using the following formula: `normalized_image = image / 255.0`.

def normalize_image(image):
    normalized_image = image / 255.0
    return normalized_image

normalized_image = normalize_image(resized_image)
```

3. **Divide the data set**: Divide the collected data into training set, verification set and test set. The recommended division ratio is: 70% training set, 15% validation set, and 15% test set. The split can be done using the `train_test_split` function. Sample code:

from sklearn.model_selection import train_test_split

def split_data(images, labels, train_ratio, val_ratio, test_ratio):
    train_images, temp_images, train_labels, temp_labels = train_test_split(images, labels, test_size=val_ratio + test_ratio, stratify=labels)
    val_images, test_images, val_labels, test_labels = train_test_split(temp_images, temp_labels, test_size=test_ratio / (val_ratio + test_ratio), stratify=temp_labels)
    return train_images, train_labels, val_images, val_labels, test_images, test_labels

train_images, train_labels, val_images, val_labels, test_images, test_labels = split_data(images, labels, 0.7, 0.15, 0.15)
```

## 3. Data Augmentation

In order to improve the generalization ability of the model, data augmentation techniques can be used. Common data augmentation methods include random rotation, flipping, cropping, adding noise, etc. Data augmentation can be done using the `torchvision.transforms` module. Sample code:

import torch
from torchvision import transforms

def get_transforms(train=True):
    if train:
        transform = transforms.Compose([
            transforms.RandomHorizontalFlip(),
            transforms.RandomVerticalFlip(),
            transforms.RandomRotation(30),
            transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),
            transforms.ColorJitter(brightness=0.1, contrast=0.1, saturation=0.1, hue=0.1),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456,0.406], std=[0.229, 0.224, 0.225]),
        ])
    else:
        transform = transforms.Compose([
            transforms.Resize(256),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
        ])
    return transform

train_transforms = get_transforms(train=True)
val_transforms = get_transforms(train=False)
test_transforms = get_transforms(train=False)

The above code defines a `get_transforms` function, which returns different data augmentation methods according to the input parameter `train`. The training set is augmented with random flips, rotations, cropping, and color jittering, while the validation and test sets are only resized and normalized.

So far, we have completed the data preparation part. The processed dataset can then be used for model training and evaluation.

# three model construction

In this section, we detail the model building process, including choosing a model architecture, defining loss functions and optimizers, and implementing the training and evaluation pipelines.

## 1. Choose model architecture

For the image style classification task, we can choose Convolutional Neural Network (CNN) as the basic architecture. Specifically, we can use a pre-trained CNN model (such as VGG, ResNet, etc.) as a feature extractor, and add a fully connected layer on top of it to achieve classification. We will use the PyTorch framework for implementation.

Sample code:

import torch
import torch.nn as nn
import torchvision.models as models

def create_model(num_classes, use_pretrained=True):
    model = models.resnet34(pretrained=use_pretrained)  # 使用预训练的ResNet34模型
    num_features = model.fc.in_features  # 获取全连接层的输入特征数
    model.fc = nn.Linear(num_features, num_classes)  # 替换全连接层以适应我们的分类任务
    return model

num_classes = 27  # 假设我们有27个艺术风格类别
model = create_model(num_classes)

## 2. Define loss function and optimizer

For classification tasks, we can use cross-entropy loss as the loss function. At the same time, we can choose optimizers such as stochastic gradient descent (SGD) or Adam for model optimization.

Sample code:

import torch.optim as optim

criterion = nn.CrossEntropyLoss()

# 使用SGD优化器
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

# 或使用Adam优化器
# optimizer = optim.Adam(model.parameters(), lr=0.001)

## 3. Implement the training and evaluation process

Next, we need to write the training and evaluation functions for training the model on the training set and validating it on the validation set. Here we use the PyTorch framework to achieve.

Sample code:

def train_epoch(model, dataloader, criterion, optimizer, device):
    model.train()
    running_loss = 0.0
    running_corrects = 0

    for inputs, labels in dataloader:
        inputs = inputs.to(device)
        labels = labels.to(device)

        optimizer.zero_grad()

        outputs = model(inputs)
        _, preds = torch.max(outputs, 1)
        loss = criterion(outputs, labels)

        loss.backward()
        optimizer.step()

        running_loss += loss.item() * inputs.size(0)
        running_corrects += torch.sum(preds == labels.data)

    epoch_loss = running_loss / len(dataloader.dataset)
    epoch_acc = running_corrects.double() / len(dataloader.dataset)

    return epoch_loss, epoch_acc

def evaluate(model, dataloader, criterion, device):
    model.eval()
    running_loss = 0.0
    running_corrects = 0

    with torch.no_grad():
        for inputs, labels in dataloader:
            inputs = inputs.to(device)
            labels = labels.to(device)

            outputs = model(inputs)
            _, preds = torch.max(outputs, 1)
            loss = criterion(outputs, labels)

            running_loss += loss.item() * inputs.size(0)
            running_corrects += torch.sum(preds == labels.data)

    epoch_loss = running_loss / len(dataloader.dataset)
    epoch_acc = running_corrects.double() / len(dataloader.dataset)

    return epoch_loss, epoch_acc

Now that we have defined the training and evaluation functions, we can start training the model. First, move the model and associated variables to the GPU (if available).

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = model.to(device)
criterion = criterion.to(device)

Then, execute the training loop.

num_epochs = 30

for epoch in range(num_epochs):
    train_loss, train_acc = train_epoch(model, train_dataloader, criterion, optimizer, device)
    val_loss, val_acc = evaluate(model, val_dataloader, criterion, device)

    print(f'Epoch {epoch + 1}/{num_epochs}')
    print(f'TrainLoss: {train_loss:.4f} Train Acc: {train_acc:.4f}')
    print(f'Val Loss: {val_loss:.4f} Val Acc: {val_acc:.4f}')

During training, we monitor the loss and accuracy on the validation set, and adjust the hyperparameters of the model (such as learning rate, optimizer, etc.) as needed. After training is complete, the performance of the model can be evaluated on the test set.

test_loss, test_acc = evaluate(model, test_dataloader, criterion, device)
print(f'Test Loss: {test_loss:.4f} Test Acc: {test_acc:.4f}')

So far, we have completed the model building part. Next, the model can be tuned and deployed according to actual needs.

# Four model training

In this part, we will introduce in detail how to perform model training, including data loading, data preprocessing, training loop and model saving.

## 1. Data loading and preprocessing

We will use PyTorch's `ImageFolder` class to load the data and `DataLoader` for data batch processing. At the same time, the data is preprocessed, including image scaling, cropping, normalization and other operations.

Sample code:

import torchvision.transforms as transforms
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader

# 定义数据预处理操作
data_transforms = {
    'train': transforms.Compose([
        transforms.RandomResizedCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'val': transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'test': transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
}

# 定义数据集路径
data_dir = 'path/to/your/data'
train_dir = f'{data_dir}/train'
val_dir = f'{data_dir}/val'
test_dir = f'{data_dir}/test'

# 加载数据集
train_dataset = ImageFolder(train_dir, transform=data_transforms['train'])
val_dataset = ImageFolder(val_dir, transform=data_transforms['val'])
test_dataset = ImageFolder(test_dir, transform=data_transforms['test'])

# 创建DataLoader
batch_size = 64
train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4)
val_dataloader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=4)
test_dataloader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=4)

## 2. Training loop

We have defined the `train_epoch` and `evaluate` functions in the previous question. Now, we will use these two functions for model training. First, move the model and associated variables to the GPU (if available).

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = model.to(device)
criterion = criterion.to(device)

Next, execute the training loop.

num_epochs = 30

best_val_acc = 0.0
best_model_weights = None

for epoch in range(num_epochs):
    train_loss, train_acc = train_epoch(model, train_dataloader, criterion, optimizer, device)
    val_loss, val_acc = evaluate(model, val_dataloader, criterion, device)

    # 如果验证准确率有所提高,则保存当前模型权重
    if val_acc > best_val_acc:
        best_val_acc = val_acc
        best_model_weights = model.state_dict().copy()

    print(f'Epoch {epoch + 1}/{num_epochs}')
    print(f'Train Loss: {train_loss:.4f} Train Acc: {train_acc:.4f}')
    print(f'Val Loss: {val_loss:.4f} Val Acc: {val_acc:.4f}')

# 加载具有最佳验证准确率的模型权重
model.load_state_dict(best_model_weights)

## 3. Model saving

After the training is complete, we need to save the trained model to a local file for subsequent use.

Sample code:

torch.save(model.state_dict(), 'model_weights.pth')

Additionally, the entire model structure and weights can also be saved.

torch.save(model, 'model.pth')

So far, we have completed the model training part. During training, we monitored the loss and accuracy on the validation set and tuned the model's hyperparameters as needed. After training is complete, the performance of the model can be evaluated on the test set.

test_loss, test_acc = evaluate(model, test_dataloader, criterion, device)
print(f'Test Loss: {test_loss:.4f} Test Acc: {test_acc:.4f}')

The trained model can be used in practical application scenarios, such as artistic style classification, artwork recommendation, etc. In the actual deployment process, we need to perform the same preprocessing operation on the input image, and use the trained model for inference to obtain the classification result.

# five model optimization

Model optimization refers to improving the performance of the model on the training set and validation set by adjusting various parameters and structures of the model. Here are some common model optimization methods:

## 1. Adjust the model structure

According to the needs of specific tasks, the model structure can be adjusted to achieve better performance. For example:

- Increase or decrease the number of convolutional and fully connected layers.
- Adjust the number of neurons in convolutional and fully connected layers.
- Consider using more complex models such as ResNet, Inception and DenseNet etc.

## 2. Data Augmentation

Data augmentation refers to increasing the diversity of training data by performing some random transformations on the training data. Data augmentation helps to improve the generalization ability of the model. Common data augmentation methods include:

- Random flip (horizontal or vertical).
- Random cropping.
- Random rotation.
- Random scaling.
- Color dithering.

In the sample code above, we have used `RandomResizedCrop` and `RandomHorizontalFlip` for data augmentation. More data augmentation methods can be added as required.

## 3. Regularization

Regularization is a method to improve the generalization ability of the model by adding an additional penalty term (usually the L1 or L2 norm of the model weights) to the loss function to prevent the model from overfitting. In PyTorch, L2 regularization can be achieved by setting a positive value for the `weight_decay` parameter in `optim`.

Sample code:

optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=0.0001)

## 4. Learning rate adjustment

The learning rate is an important parameter in the optimizer, which determines the update speed of the model weights. An appropriate learning rate can accelerate model convergence and improve model performance. Usually, we can dynamically adjust the learning rate during training, for example:

- Use learning rate decay: After a fixed number of training epochs, multiply the learning rate by a decay factor.
- Warm up with learning rate: use a small learning rate at the beginning of training and gradually increase it.
- Use adaptive learning rate adjustment strategies, such as Adam, RMSprop, etc.

In PyTorch, these policies can be implemented using the learning rate scheduler in `torch.optim.lr_scheduler`.

Sample code:

from torch.optim.lr_scheduler import StepLR

optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
scheduler = StepLR(optimizer, step_size=10, gamma=0.1)

for epoch in range(num_epochs):
    train_loss, train_acc = train_epoch(model, train_dataloader, criterion, optimizer, device)
    val_loss, val_acc = evaluate(model, val_dataloader, criterion, device)

    # 更新学习率
    scheduler.step()

## 5. Early Stopping

Early stopping is a strategy to avoid overfitting. When the performance of the model on the verification set does not improve significantly in multiple consecutive training cycles, the training is terminated early. This saves computational resources while preventing the model from overfitting on the training set. To implement early stopping, a counter needs to be added to the training loop to track the number of training epochs where the validation accuracy did not improve. When the counter reaches a preset threshold, the training is terminated.

Sample code:

num_epochs = 30
patience = 5
num_epochs_no_improve = 0
best_val_acc = 0.0
best_model_weights = None

for epoch in range(num_epochs):
    train_loss, train_acc = train_epoch(model, train_dataloader, criterion, optimizer, device)
    val_loss, val_acc = evaluate(model, val_dataloader, criterion, device)

    # 如果验证准确率有所提高,则保存当前模型权重,并重置计数器
    if val_acc > best_val_acc:
        best_val_acc = val_acc
        best_model_weights = model.state_dict().copy()
        num_epochs_no_improve = 0
    else:
        num_epochs_no_improve += 1

    print(f'Epoch {epoch + 1}/{num_epochs}')
    print(f'Train Loss: {train_loss:.4f} Train Acc: {train_acc:.4f}')
    print(f'Val Loss: {val_loss:.4f} Val Acc: {val_acc:.4f}')

    # 如果连续多个训练周期中,验证准确率没有提高,则提前终止训练
    if num_epochs_no_improve == patience:
        print(f'Early stopping at epoch {epoch + 1}')
        break

# 加载具有最佳验证准确率的模型权重
model.load_state_dict(best_model_weights)

## 6. Using the pre-trained model (Transfer Learning)

When the amount of training data is limited, pre-trained models can be used for transfer learning. Pre-trained models are pre-trained on large datasets and have good feature extraction capabilities. We can use the convolutional layer part of the pre-trained model as a feature extractor, then add a brand new classifier, and fine-tune it on our task.

In PyTorch, you can use the pre-trained models in `torchvision.models` for transfer learning.

Sample code:

import torchvision.models as models

# 加载预训练模型
pretrained_model = models.resnet18(pretrained=True)

# 用一个新的全连接层替换模型的最后一层
num_classes = 10
num_features = pretrained_model.fc.in_features
pretrained_model.fc = torch.nn.Linear(num_features, num_classes)

After that, model training and optimization can be done. Note that when fine-tuning the pre-trained model, you can use a smaller learning rate to avoid destroying the weights of the pre-trained model.

These model optimization strategies can be combined to improve the performance of the model on a specific task. In practical applications, it is necessary to select an appropriate optimization strategy according to the specific situation and needs.

# Six model evaluation

Model evaluation refers to using an independent test set to evaluate the performance of the model after the model training is completed. This helps us understand how well the model generalizes on unseen data. Here are some common model evaluation methods:

## 1. Accuracy

Accuracy is one of the most commonly used evaluation metrics in classification problems. It represents the proportion of the number of samples correctly predicted by the model to the total number of samples. The calculation formula is:

准确率 = (正确预测的样本数) / (总样本数)

In PyTorch, the accuracy can be calculated using the following code:

def compute_accuracy(model, dataloader, device):
    correct = 0
    total = 0
    model.eval()
    with torch.no_grad():
        for data in dataloader:
            inputs, labels = data
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = model(inputs)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    return correct / total

## 2. Confusion Matrix

A confusion matrix is ​​a matrix that shows the classification results of a model by comparing the model's predicted labels with the actual labels. In a multi-class classification problem, each row of the confusion matrix corresponds to a true class and each column corresponds to a predicted class. The elements on the main diagonal of the confusion matrix represent the number of samples that were correctly classified, and the other elements represent the number of samples that were misclassified.

In Python, the confusion matrix can be calculated using the `sklearn.metrics.confusion_matrix` function:

import numpy as np
from sklearn.metrics import confusion_matrix

def compute_confusion_matrix(model, dataloader, device):
    true_labels = []
    predictions = []
    model.eval()
    with torch.no_grad():
        for data in dataloader:
            inputs, labels = data
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = model(inputs)
            _, predicted = torch.max(outputs.data, 1)
            true_labels.extend(labels.cpu().numpy())
            predictions.extend(predicted.cpu().numpy())
    return confusion_matrix(true_labels, predictions)

## 3. Precision, Recall and F1 Score

Precision, recall, and F1 score are three other commonly used evaluation metrics in classification problems. They respectively measure the accuracy of the model in predicting positive cases, the ability of the model to identify positive cases, and the harmonic mean of the two.

- Precision: The ratio of the number of samples correctly predicted as positive to the number of all samples predicted as positive.
- Recall: The ratio of the number of samples correctly predicted as positive to the number of all samples that are actually positive.
- F1 score: Harmonized mean of precision and recall.

In Python, these metrics can be calculated using the `precision_score`, `recall_score` and `f1_score` functions in `sklearn.metrics`:

from sklearn.metrics import precision_score, recall_score, f1_score

def compute_metrics(y_true, y_pred, average='macro'):
    precision = precision_score(y_true, y_pred, average=average)
    recall = recall_score(y_true, y_pred, average=average)
    f1 = f1_score(y_true, y_pred, average=average)
    return precision, recall, f1

## 4. ROC curve and AUC value

The ROC curve (Receiver Operating Characteristic Curve) is a graphical representation for evaluating model performance in binary classification problems. It plots the performance of the model's True Positive Rate (TPR) and False Positive Rate (False Positive Rate, FPR) under different thresholds on a two-dimensional plane, describing the model's performance between positive and negative examples. balance between. The AUC value (Area Under the Curve) is the area under the ROC curve, which is used to measure the classification performance of the model. The closer the AUC value is to 1, the better the performance of the model; the closer the AUC value is to 0.5, the closer the performance of the model is to random guessing.

In Python, you can use the `roc_curve` and `roc_auc_score` functions in `sklearn.metrics` to calculate the ROC curve and AUC value:

import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, roc_auc_score

def plot_roc_curve_and_compute_auc(y_true, y_prob):
    fpr, tpr, thresholds = roc_curve(y_true, y_prob)
    auc = roc_auc_score(y_true, y_prob)

    plt.figure()
    plt.plot(fpr, tpr, label='ROC curve (area = %0.2f)' % auc)
    plt.plot([0, 1], [0, 1], 'k--')
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('Receiver Operating Characteristic')
    plt.legend(loc="lower right")
    plt.show()

    return auc

Note that computing ROC curves and AUC values ​​requires predicting probabilities, not predicting categories. In PyTorch, the predicted probability can be obtained by passing the output of the model to the `torch.softmax` function.

## 5. Cross-Validation

Cross-validation is a method used to evaluate the performance of a model, which divides the data set into k subsets and performs k training and validation processes. In each pass, a subset is used as the validation set and the remaining subset is used as the training set. By calculating the average of k validation results, we can obtain a more stable and accurate evaluation of model performance.

In Python, cross-validation can be performed using the `KFold` or `StratifiedKFold` classes from `sklearn.model_selection`:

from sklearn.model_selection import KFold, StratifiedKFold

def cross_validation(model, X, y, k_fold=5, random_state=None):
    kfold = KFold(n_splits=k_fold, shuffle=True, random_state=random_state)
    # 或者使用 StratifiedKFold 保持每个子集中类别的分布与原数据集一致
    # kfold = StratifiedKFold(n_splits=k_fold, shuffle=True, random_state=random_state)
    
    scores = []
    for train_idx, val_idx in kfold.split(X, y):
        X_train, X_val = X[train_idx], X[val_idx]
        y_train, y_val = y[train_idx], y[val_idx]
        
        # 训练并评估模型
        model.fit(X_train, y_train)
        score = model.score(X_val, y_val)
        scores.append(score)
    
    return np.mean(scores), np.std(scores)

In cross-validation, different folding numbers (k values) and data set partition strategies (such as `KFold` and `StratifiedKFold`) can be selected to meet the needs of different scenarios.

The above are some common model evaluation methods. In practical applications, appropriate evaluation indicators can be selected according to specific problems and needs.

7. Summary and Outlook

In this part, summarize the process of the whole project, including data preparation, model construction, training, optimization and evaluation, etc. At the same time, discuss possible improvement directions and future work, such as trying more model structures, optimization techniques, etc.

Guess you like

Origin blog.csdn.net/a871923942/article/details/130936189