深度学习实战基础案例——卷积神经网络(CNN)基于DenseNet的眼疾检测|第4例

news2025/2/18 21:04:51

文章目录

  • 前言
  • 一、数据准备
  • 二、项目实战
    • 2.1 设置GPU
    • 2.2 数据加载
    • 2.3 数据预处理
    • 2.4 数据划分
    • 2.5 搭建网络模型
    • 2.6 构建densenet121
    • 2.7 训练模型
    • 2.8 结果可视化
  • 三、UI设计
  • 四、结果展示
  • 总结

前言

在当今社会,眼科疾病尤其是白内障对人们的视力健康构成了严重威胁。白内障是全球范围内导致失明的主要原因之一,早期准确的诊断对于疾病的治疗和患者的预后至关重要。传统的白内障检测方法主要依赖于眼科医生的专业判断,这不仅需要大量的人力和时间,而且诊断结果可能会受到医生经验和主观因素的影响。
随着深度学习技术的飞速发展,其在医疗图像分析领域展现出了巨大的潜力。卷积神经网络(CNN)作为深度学习中的重要模型,已经在多种医疗图像识别任务中取得了显著的成果,如肿瘤检测、疾病分类等。利用 CNN 对眼科图像进行分析,可以辅助医生更快速、准确地进行疾病诊断。
本文将详细介绍如何使用基于 DenseNet 的卷积神经网络进行白内障疾病检测。通过这个实战案例,不仅可以帮助读者了解 DenseNet 的原理和应用,还能掌握利用深度学习进行医疗图像分析的基本流程和方法,为进一步开展相关研究和实践提供参考。

一、数据准备

本案例使用的数据集是retina_dataset|眼科疾病数据集。
数据集下载地址:点击这里
Retina Dataset的构建基于眼底图像的分类需求,涵盖了四种主要的眼科疾病类别:正常、白内障、青光眼和视网膜疾病。数据集通过收集和整理不同患者的视网膜图像,确保每类疾病均有代表性样本。图像数据经过标准化处理,以保证在不同设备和条件下获取的图像具有一致性,从而为后续的分类和分析提供了坚实的基础。

在这里插入图片描述

二、项目实战


我的环境:

  • 基础环境:Python3.9
  • 编译器:PyCharm 2024
  • 深度学习框架:Pytorch2.0

2.1 设置GPU

import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision
from torchvision import transforms, datasets
import os,PIL,pathlib,warnings

warnings.filterwarnings("ignore")             #忽略警告信息

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device

2.2 数据加载

import os,PIL,random,pathlib

data_dir = '数据路径'
data_dir = pathlib.Path(data_dir)

data_paths  = list(data_dir.glob('*'))
classeNames = [str(path).split("\\")[1] for path in data_paths]

2.3 数据预处理

train_transforms = transforms.Compose([
    transforms.Resize([224, 224]),  # 将输入图片resize成统一尺寸
    # transforms.RandomHorizontalFlip(), # 随机水平翻转
    transforms.ToTensor(),          # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    transforms.Normalize(           # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
        mean=[0.485, 0.456, 0.406], 
        std=[0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
])

test_transform = transforms.Compose([
    transforms.Resize([224, 224]),  # 将输入图片resize成统一尺寸
    transforms.ToTensor(),          # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    transforms.Normalize(           # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
        mean=[0.485, 0.456, 0.406], 
        std=[0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
])
total_data = datasets.ImageFolder(data_dir,transform=train_transforms)

2.4 数据划分

train_size = int(0.8 * len(total_data))
test_size  = len(total_data) - train_size
train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])
batch_size = 32

train_dl = torch.utils.data.DataLoader(train_dataset,
                                           batch_size=batch_size,
                                           shuffle=True)
test_dl = torch.utils.data.DataLoader(test_dataset,
                                          batch_size=batch_size,
                                          shuffle=True)

2.5 搭建网络模型

import torch.nn as nn
import torch
from torch import mean, max

class _DenseLayer(nn.Module):
    def __init__(self, num_input_features, growth_rate, bn_size, drop_rate=0):
        super(_DenseLayer, self).__init__()
        self.drop_rate = drop_rate
        self.dense_layer = nn.Sequential(
            nn.BatchNorm2d(num_input_features),
            nn.ReLU(),
            nn.Conv2d(in_channels=num_input_features, out_channels=bn_size * growth_rate, kernel_size=1, stride=1,
                      padding=0),
            Inceptionnext(bn_size * growth_rate, bn_size * growth_rate, kernel_size=3),
            CBAMBlock("FC", 5, channels=bn_size * growth_rate, ratio=9),
            nn.Conv2d(in_channels=bn_size * growth_rate, out_channels=growth_rate, kernel_size=1, stride=1, padding=0)
        )
        self.dropout = nn.Dropout(p=self.drop_rate)

    def forward(self, x):
        y = self.dense_layer(x)
        if self.drop_rate > 0:
            y = self.dropout(y)

        return torch.concat([x, y], dim=1)


class _DenseBlock(nn.Module):
    def __init__(self, num_layers, num_input_features, bn_size, growth_rate, drop_rate=0):
        super(_DenseBlock, self).__init__()
        layers = []
        for i in range(num_layers):
            layers.append(_DenseLayer(num_input_features + i * growth_rate, growth_rate, bn_size, drop_rate))
        self.layers = nn.Sequential(*layers)

    def forward(self, x):
        return self.layers(x)


class _TransitionLayer(nn.Module):
    def __init__(self, num_input_features, num_output_features):
        super(_TransitionLayer, self).__init__()
        self.transition_layer = nn.Sequential(
            nn.BatchNorm2d(num_input_features),
            nn.ReLU(),
            nn.Conv2d(in_channels=num_input_features, out_channels=num_output_features, kernel_size=1, stride=1,
                      padding=0),
            nn.AvgPool2d(kernel_size=2, stride=2)
        )

    def forward(self, x):
        return self.transition_layer(x)


class DenseNet(nn.Module):
    def __init__(self, num_init_features=64, growth_rate=32, blocks=(6, 12, 24, 16), bn_size=4, drop_rate=0,
                 num_classes=1000):
        super(DenseNet, self).__init__()

        self.features = nn.Sequential(
            nn.Conv2d(in_channels=3, out_channels=num_init_features, kernel_size=7, stride=2, padding=3),
            nn.BatchNorm2d(num_init_features),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        )

        num_features = num_init_features
        self.layer1 = _DenseBlock(num_layers=blocks[0], num_input_features=num_features, growth_rate=growth_rate,
                                  bn_size=bn_size, drop_rate=drop_rate)
        num_features = num_features + blocks[0] * growth_rate
        self.transtion1 = _TransitionLayer(num_input_features=num_features, num_output_features=num_features // 2)

        num_features = num_features // 2
        self.layer2 = _DenseBlock(num_layers=blocks[1], num_input_features=num_features, growth_rate=growth_rate,
                                  bn_size=bn_size, drop_rate=drop_rate)
        num_features = num_features + blocks[1] * growth_rate
        self.transtion2 = _TransitionLayer(num_input_features=num_features, num_output_features=num_features // 2)

        num_features = num_features // 2
        self.layer3 = _DenseBlock(num_layers=blocks[2], num_input_features=num_features, growth_rate=growth_rate,
                                  bn_size=bn_size, drop_rate=drop_rate)
        num_features = num_features + blocks[2] * growth_rate
        self.transtion3 = _TransitionLayer(num_input_features=num_features, num_output_features=num_features // 2)

        num_features = num_features // 2
        self.layer4 = _DenseBlock(num_layers=blocks[3], num_input_features=num_features, growth_rate=growth_rate,
                                  bn_size=bn_size, drop_rate=drop_rate)
        num_features = num_features + blocks[3] * growth_rate

        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(num_features, num_classes)

    def forward(self, x):
        x = self.features(x)

        x = self.layer1(x)
        x = self.transtion1(x)
        x = self.layer2(x)
        x = self.transtion2(x)
        x = self.layer3(x)
        x = self.transtion3(x)
        x = self.layer4(x)

        x = self.avgpool(x)
        y = torch.flatten(x, start_dim=1)
        x = self.fc(y)

        return x

2.6 构建densenet121

device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))

densenet121 = DenseNet(blocks=(6,12,24,16),
                       num_classes=len(classeNames))  

model = densenet121.to(device)

2.7 训练模型

# 训练循环
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)  # 训练集的大小
    num_batches = len(dataloader)   # 批次数目, (size/batch_size,向上取整)

    train_loss, train_acc = 0, 0  # 初始化训练损失和正确率
    
    for X, y in dataloader:  # 获取图片及其标签
        X, y = X.to(device), y.to(device)
        
        # 计算预测误差
        pred = model(X)          # 网络输出
        loss = loss_fn(pred, y)  # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失
        
        # 反向传播
        optimizer.zero_grad()  # grad属性归零
        loss.backward()        # 反向传播
        optimizer.step()       # 每一步自动更新
        
        # 记录acc与loss
        train_acc  += (pred.argmax(1) == y).type(torch.float).sum().item()
        train_loss += loss.item()
            
    train_acc  /= size
    train_loss /= num_batches

    return train_acc, train_loss

def test (dataloader, model, loss_fn):
    size        = len(dataloader.dataset)  # 测试集的大小
    num_batches = len(dataloader)          # 批次数目, (size/batch_size,向上取整)
    test_loss, test_acc = 0, 0
    
    # 当不进行训练时,停止梯度更新,节省计算内存消耗
    with torch.no_grad():
        for imgs, target in dataloader:
            imgs, target = imgs.to(device), target.to(device)
            
            # 计算loss
            target_pred = model(imgs)
            loss        = loss_fn(target_pred, target)
            
            test_loss += loss.item()
            test_acc  += (target_pred.argmax(1) == target).type(torch.float).sum().item()

    test_acc  /= size
    test_loss /= num_batches

    return test_acc, test_loss

import copy

optimizer  = torch.optim.Adam(model.parameters(), lr= 1e-4)
loss_fn    = nn.CrossEntropyLoss() # 创建损失函数

epochs     = 20

train_loss = []
train_acc  = []
test_loss  = []
test_acc   = []

best_acc = 0    # 设置一个最佳准确率,作为最佳模型的判别指标

for epoch in range(epochs):
    
    model.train()
    epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, optimizer)
    
    model.eval()
    epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
    
    # 保存最佳模型到 best_model
    if epoch_test_acc > best_acc:
        best_acc   = epoch_test_acc
        best_model = copy.deepcopy(model)
    
    train_acc.append(epoch_train_acc)
    train_loss.append(epoch_train_loss)
    test_acc.append(epoch_test_acc)
    test_loss.append(epoch_test_loss)
    
    # 获取当前的学习率
    lr = optimizer.state_dict()['param_groups'][0]['lr']
    
    template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}, Lr:{:.2E}')
    print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, 
                          epoch_test_acc*100, epoch_test_loss, lr))
    
# 保存最佳模型到文件中
PATH = './best_model.pth'  # 保存的参数文件名
torch.save(best_model.state_dict(), PATH)

print('Done')

2.8 结果可视化

import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings("ignore")               #忽略警告信息

epochs_range = range(epochs)

plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)

plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Test Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training and Test Loss')
plt.show()

在这里插入图片描述

三、UI设计

这里使用QT Designder设计了一个简易的UI界面,可以很方便的进行使用。
在这里插入图片描述
UI.py文件如下


import test
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QFileDialog, QMessageBox

imgName=None


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(649, 559)
        self.label = QtWidgets.QLabel(Form)
        self.label.setGeometry(QtCore.QRect(120, 20, 331, 41))
        self.label.setStyleSheet("\n"
"font: 22pt \"华文彩云\";")
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(Form)
        self.label_2.setGeometry(QtCore.QRect(120, 130, 311, 251))
        self.label_2.setStyleSheet("border-image: url(:/新前缀/img.png);")
        self.label_2.setText("")
        self.label_2.setObjectName("label_2")
        self.label_4 = QtWidgets.QLabel(Form)
        self.label_4.setGeometry(QtCore.QRect(60, 440, 72, 15))
        self.label_4.setObjectName("label_4")
        self.textEdit = QtWidgets.QTextEdit(Form)
        self.textEdit.setGeometry(QtCore.QRect(140, 440, 211, 91))
        self.textEdit.setObjectName("textEdit")
        self.layoutWidget = QtWidgets.QWidget(Form)
        self.layoutWidget.setGeometry(QtCore.QRect(60, 400, 221, 31))
        self.layoutWidget.setObjectName("layoutWidget")
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.layoutWidget)
        self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.label_3 = QtWidgets.QLabel(self.layoutWidget)
        self.label_3.setObjectName("label_3")
        self.horizontalLayout_2.addWidget(self.label_3)
        self.lineEdit_2 = QtWidgets.QLineEdit(self.layoutWidget)
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.horizontalLayout_2.addWidget(self.lineEdit_2)
        self.layoutWidget1 = QtWidgets.QWidget(Form)
        self.layoutWidget1.setGeometry(QtCore.QRect(30, 70, 591, 41))
        self.layoutWidget1.setObjectName("layoutWidget1")
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.layoutWidget1)
        self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.lineEdit = QtWidgets.QLineEdit(self.layoutWidget1)
        self.lineEdit.setObjectName("lineEdit")
        self.horizontalLayout.addWidget(self.lineEdit)
        self.pushButton = QtWidgets.QPushButton(self.layoutWidget1)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout.addWidget(self.pushButton)
        self.horizontalLayout_3.addLayout(self.horizontalLayout)
        self.pushButton_2 = QtWidgets.QPushButton(self.layoutWidget1)
        self.pushButton_2.setObjectName("pushButton_2")
        self.horizontalLayout_3.addWidget(self.pushButton_2)

        self.pushButton.clicked.connect(self.openImage)
        self.pushButton_2.clicked.connect(self.inferImage)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.label.setText(_translate("Form", "白内障检测系统"))
        self.label_4.setText(_translate("Form", "诊断建议:"))
        self.label_3.setText(_translate("Form", "识别结果:"))
        self.pushButton.setText(_translate("Form", "打开文件"))
        self.pushButton_2.setText(_translate("Form", "开始识别"))

    def openImage(self):  # 选择本地图片上传
        global imgName  # 这里为了方便别的地方引用图片路径,我们把它设置为全局变量
        imgName, imgType = QFileDialog.getOpenFileName(self, "打开图片", "",
                                                       "*.jpg;*.png;;All Files(*)")  # 弹出一个文件选择框,第一个返回值imgName记录选中的文件路径+文件名,第二个返回值imgType记录文件的类型
        jpg = QtGui.QPixmap(imgName).scaled(self.label_2.width(),
                                            self.label_2.height())  # 通过文件路径获取图片文件,并设置图片长宽为label控件的长宽
        self.label_2.setPixmap(jpg)  # 在label控件上显示选择的图片
        self.lineEdit.setText(imgName)  # 显示所选图片的本地路径

    def inferImage(self):
        global imgName
        if imgName is None or imgName == '':
            QMessageBox.information(self, "Error!", "请先选择图片!", QMessageBox.Ok)
            return
        a1, a2 = test.infer(imgName)
        self.lineEdit_2.setText(a1)
        self.textEdit.setText(a2)
import asd_rc

四、结果展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

通过本次案例,我们可以对深度学习设计程序的流程有一个简单清楚的认知,以便我们将来构建其它深度学习系统可以更加得心应手。

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

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

相关文章

(一)Axure制作移动端登录页面

你知道如何利用Axure制作移动端登录页面吗?Axure除了可以制作Web端页面,移动端也是可以的哦,下面我们就一起来看一下Axure制作移动端登录页面的过程吧。 第一步:从元件中拖入一个矩形框,并设置其尺寸为:37…

【Linux】【进程】epoll内核实现

【Linux】【进程】epoll内核实现 1 epoll提供的三个函数 1.1 epoll_create(int size); epoll_create()成功返回内核事件表的文件描述符,失败返回-1size 参数现在并不起作用 1.2 epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); epoll_ctl()成…

ICRA-2025 | 具身导航如何跨越地形障碍?SARO:通过视觉语言模型实现地形穿越

作者:Shaoting Zhu, Derun Li, Linzhan Mou, Yong Liu, Ningyi Xu, Hang Zhao 单位:清华大学交叉信息研究院,上海交通大学电子信息与电气工程学院,浙江大学计算机科学与技术学院,宾夕法尼亚大学GRASP实验室&#xff0…

当 LSTM 遇上 ARIMA!!

大家好,我是小青 ARIMA 和 LSTM 是两种常用于时间序列预测的模型,各有优劣。 ARIMA 擅长捕捉线性关系,而 LSTM 擅长处理非线性和长时间依赖的关系。将ARIMA 和 LSTM 融合,可以充分发挥它们各自的优势,构建更强大的时…

终结磁盘空间紧张局面,针对性处理重复、无用文件

软件介绍 在如今这个数字化浪潮汹涌的时代,咱们的电脑存储空间就像一个杂乱无章的储物间,被各种各样的重复文件塞得满满当当。这些重复文件,犹如隐藏在暗处的 “空间小偷”,悄无声息地吞噬着宝贵的硬盘空间,使得原本井…

高校LabVIEW开发调试中的常见问题

在高校进行LabVIEW开发调试时,常常面临硬件选型不当、方案设计不合理、布线不专业以及人员流动性强等问题。这些问题可能影响项目的进展和质量。本文将总结这些问题,并给出具体的解决方案,帮助学生和团队更高效地开展开发工作。 ​ 1. 硬件选…

【故障处理】- RMAN-06593: platform name ‘Linux x86 64-bitElapsed: 00:00:00.00‘

【故障处理】- RMAN-06593: platform name Linux x86 64-bitElapsed: 00:00:00.00 一、概述二、报错原因三、解决方法 一、概述 使用xtts迁移,在目标端进行恢复时,遇到RMAN-06593: platform name Linux x86 64-bitElapsed: 00:00:00.00’报错。 二、报错…

K8S下载离线安装包所需文件

下载相关文件 官网下载地址集合https://kubernetes.io/zh-cn/releases/download/ 下载相关镜像 官网镜像描述 所有 Kubernetes 容器镜像都被部署到 registry.k8s.io 容器镜像仓库。 容器镜像支持架构registry.k8s.io/kube-apiserver:v1.32.0amd64, arm, arm64, ppc64le, …

如何使用Java语言在Idea和Android中分别建立服务端和客户端实现局域网聊天

手把手教你用Java语言在Idea和Android中分别建立服务端和客户端实现局域网聊天 目录 文章目录 手把手教你用**Java**语言在**Idea**和**Android**中分别建立**服务端**和**客户端**实现局域网聊天**目录**[toc]**基本实现****问题分析****服务端**Idea:结构预览Server类代码解…

ArcGIS注册开发账号及API KEY

注册与激活 Sign up | ArcGIS Location Platform 填写信息,然后邮箱收到激活邮件,激活,再补充信息。 参考 Tutorial: Create an API key | Documentation | Esri Developer 产生API KEY Tutorial: Create an API key | Documentation |…

java八股---java面向对象

面向对象 面向对象概述 面向对象和面向过程的区别 面向过程: 优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、 Linux/Unix等一般采用面向过程开发,性能…

《Deepseek入门到精通》2.0版本《Deepseek赋能职场应用》清华大学

🚀 《Deepseek入门到精通》2.0版本重磅发布! 📚 全新升级,赋能职场应用! 经过多次改版与优化,《Deepseek入门到精通》2.0版本已经正式上线!这不仅是一份技术指南,更是你提升职场竞争…

使用c++实现红黑树的构建和插入

1.红黑树简介: 红黑树实际上和AVL都属于一棵用于存储数据的平衡二叉搜索树,但是这棵树并不是使用平衡因子去维持平衡的,而是结合限制条件对结点标红标黑去让树达到类似平衡的效果。 2.红黑树的限制条件和效率分析: 2.1限制条件…

在大型语言模型(LLM)框架内Transformer架构与混合专家(MoE)策略的概念整合

文章目录 传统的神经网络框架存在的问题一. Transformer架构综述1.1 transformer的输入1.1.1 词向量1.1.2 位置编码(Positional Encoding)1.1.3 编码器与解码器结构1.1.4 多头自注意力机制 二.Transformer分步详解2.1 传统词向量存在的问题2.2 详解编解码…

Jenkins项目CICD流程

Jenkins项目流程:1.配置git环境 git config --...2.把前后端的目录初始化位本地工作目录 #git init3.提交到本地git #git add ./ git commit -m "" git tag v14.然后提交到远程git(通过,用户,群组,项目,管理项目)git remote add origin http://...git push -…

【IDEA】2017版本的使用

目录 一、常识 二、安装 1. 下载IDEA2017.exe 2. 安装教程 三、基本配置 1. 自动更新关掉 2. 整合JDK环境 3. 隐藏.idea文件夹和.iml等文件 四、创建Java工程 1. 新建项目 2. 创建包结构,创建类,编写main主函数,在控制台输出内容。…

Git指南-从入门到精通

代码提交和同步命令 流程图如下: 第零步: 工作区与仓库保持一致第一步: 文件增删改,变为已修改状态第二步: git add ,变为已暂存状态 bash $ git status $ git add --all # 当前项目下的所有更改 $ git add . # 当前目录下的所有更改 $ g…

Spring boot(maven) - Mybatis 超级入门版

前言: 通过实践而发现真理,又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识,又从理性认识而能动地指导革命实践,改造主观世界和客观世界。实践、认识、再实践、再认识,这种形式,循环往…

Spark 性能优化 (三):RBO 与 CBO

1. RBO 的核心概念 在 Apache Spark 的查询优化过程中,规则优化(Rule-Based Optimization, RBO) 是 Catalyst 优化器的一个关键组成部分。它主要依赖于一组固定的规则进行优化,而不是基于统计信息(如 CBO - Cost-Base…

读 DeepSeek-R1 论文笔记

DeepSeek-R1:通过强化学习激发大语言模型的推理能力 DeepSeek-AI 摘要 我们推出第一代推理模型DeepSeek-R1-Zero和DeepSeek-R1。DeepSeek-R1-Zero作为无需监督微调(SFT)预训练阶段、直接通过大规模强化学习(RL)训练的基础模型,展现出卓越的推理能力。…