当前位置: 首页 > news >正文

多模态融合:阿尔茨海默病检测

  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍖 原作者:K同学啊

一、实验介绍

本实验包含 645 名阿尔茨海默病受试者,分为 AD、CN 和 MCI 组,数据集包含 3D MRI 图像与一份CSV数据,MRI数据与CSV中的数据通过受试者ID进行关联。
在这里插入图片描述

  • AD (Alzheimer’s Disease):指的是已经确诊的阿尔茨海默病,这是一种神经退行性疾病,通常表现为认知功能的显著下降,特别是记忆丧失、语言问题、以及其他认知能力的严重损害。AD 是痴呆最常见的原因。
  • CN (Cognitively Normal):代表认知正常,指的是没有出现明显的认知障碍或记忆问题的人。这类人在认知测试中表现正常,不显示出认知功能的下降。
  • MCI (Mild Cognitive Impairment):指的是轻度认知障碍,这是介于认知正常和痴呆之间的一个中间阶段。MCI 的患者虽然表现出一些认知功能的下降,特别是在记忆方面,但这些问题尚未严重到影响日常生活的程度。MCI 有可能发展为阿尔茨海默病,但并不是所有的 MCI 患者都会最终患上 AD。

二、实验环境

  • 语言环境:python 3.8
  • 编译器:Pycharm
  • 深度学习环境:Pytorch
  • 显卡:GeForce RTX 4090D

三、准备工作

1. 设置GPU

import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision
from torchvision import transforms, datasetsimport os, PIL, pathlib, random
import pandas as pd
import nibabel as nib
import matplotlib.pyplot as plt
from torch.utils.data import Dataset
import torch.utils.data as data
import numpy as np
import torch.nn.functional as F# 设置GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

2. 导入CSV数据

df = pd.read_csv("./K1data/ADNI_1p5T_Filtered.csv",usecols=["Image Data ID", "Group", "Sex", "Age"])
df.head(3)

在这里插入图片描述

# 对sex列做one-hot编码
df = pd.get_dummies(df, columns=['Sex'])
df.head(3)

在这里插入图片描述

3. 导入mgz数据

labels = []
img_paths = []
strnums = []for index, row in df.iterrows():labels.append(row['Group'])# 合成文件路径img_path = "./K1data/" + row['Group'] + "/" + row['Image Data ID'] + ".nii_aseg_deep.mgz"img_paths.append(img_path)strnums.append([row['Age'], row['Sex_F'], row['Sex_M']])strnums[:3]

在这里插入图片描述

# 定义数据文件夹路径
data_path = r'./K1data/AD/I48590.nii_aseg_deep.mgz'  # 替换为AD文件夹的实际路径img_data = nib.load(data_path).get_fdata()# 将 NumPy 数组转换为 PyTorch 张量
img_tensor = torch.tensor(img_data, dtype=torch.float32)
img_tensor.shape

在这里插入图片描述

4. 数据可视化

# 加载 .mgz 文件
def load_mgz_file(file_path):return nib.load(file_path).get_fdata()# 可视化三个方向的切片
def visualize_slices(img_data):# 选择每个方向的中间切片slice_axial = img_data[img_data.shape[0] // 2, :, :]slice_sagittal = img_data[:, img_data.shape[1] // 2, :]slice_coronal = img_data[:, :, img_data.shape[2] // 2]# 创建子图并显示fig, axes = plt.subplots(1, 3, figsize=(12, 4))axes[0].imshow(slice_axial.T, cmap='gray', origin='lower')axes[0].set_title('Axial')axes[1].imshow(slice_sagittal.T, cmap='gray', origin='lower')axes[1].set_title('Sagittal')axes[2].imshow(slice_coronal.T, cmap='gray', origin='lower')axes[2].set_title('Coronal')# 隐藏坐标轴for ax in axes:ax.axis('off')plt.show()# 示例:加载并显示 MRI 数据
file_path = './K1data/AD/I31143.nii_aseg_deep.mgz'  # 替换为实际文件路径
img_data = load_mgz_file(file_path)
visualize_slices(img_data)  # 轴向切片

在这里插入图片描述

四、数据预处理

1. 构建数据集

class MyDataset(Dataset):def __init__(self, all_labels, img_paths, strnums, transform):self.img_labels = all_labels  # 获取标签信息self.img_dir = img_paths  # 图像目录路径self.strnums = strnums  # 获取数值信息self.transform = transform  # 目标转换函数def __len__(self):return len(self.img_labels)def __getitem__(self, index):image = nib.load(self.img_dir[index]).get_fdata()if self.img_labels[index] == "CN":label = torch.tensor(0, dtype=torch.long)elif self.img_labels[index] == "MCI":label = torch.tensor(1, dtype=torch.long)else:label = torch.tensor(2, dtype=torch.long)strnum = torch.tensor(self.strnums[index], dtype=torch.float32)if self.transform:image = self.transform(image)return image, strnum, label  # 返回图像和标签

2. 数据预处理

def interpolate_volume(vol, size, mode='trilinear', align_corners=True):vol = torch.tensor(vol, dtype=torch.float32)vol = vol.unsqueeze(0).unsqueeze(0)  # 扩展为 (1, 1, D, H, W) 的形状vol_resized = F.interpolate(vol, size=size,mode=mode, align_corners=align_corners)return vol_resized.squeeze(0).squeeze(0)  # 去掉多余的维度,返回 (D_target, H_target, W_target)class ToTensor3D:"""将 3D numpy.ndarray 转换为 PyTorch Tensor,并调整维度顺序为 (C, D, H, W)。"""def __call__(self, img):# 确保输入为 numpy.ndarrayif isinstance(img, np.ndarray):img = torch.from_numpy(img).float()# 调整维度顺序为 (C, D, H, W),这里我们假设只有一个通道return img.unsqueeze(0)  # 添加通道维度class Normalize3D:"""对 3D 图像进行标准化处理。"""def __init__(self, mean, std):self.mean = torch.tensor(mean).view(-1, 1, 1, 1)  # 调整维度匹配self.std = torch.tensor(std).view(-1, 1, 1, 1)def __call__(self, tensor):return (tensor - self.mean) / self.stddef train_transforms(img):"""3D 图像的训练数据转换。Args:img (numpy.ndarray): 输入 3D 图像,形状为 (D, H, W)。Returns:torch.Tensor: 转换后的图像张量,形状为 (C, D, H, W)。"""img = np.array(img)img = interpolate_volume(img, size=224).numpy()tensor = torch.tensor(img, dtype=torch.float32)# Step 3: 标准化处理# 计算适合医学图像的均值和标准差mean = tensor.mean()std = tensor.std()normalized_tensor = Normalize3D(mean=[mean], std=[std])(tensor)return normalized_tensor# 实例化数据集
total_data = MyDataset(labels, img_paths, strnums, transform=train_transforms)

3. 划分数据集

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])
train_loader = torch.utils.data.DataLoader(train_dataset,batch_size=4,shuffle=True)
test_loader = torch.utils.data.DataLoader(test_dataset,batch_size=4,shuffle=True)print("The number of images in a training set is: ", len(train_loader) * 4)
print("The number of images in a test set is: ", len(test_loader) * 4)
print("The number of batches per epoch is: ", len(train_loader))

在这里插入图片描述

for X_1, X_2, y in test_loader:print("Shape of X_1 [N, C, H, W]: ", X_1.shape)print("Shape of X_2: ", X_2.shape)print("Shape of X_2: ", X_2[:3])print("Shape of y: ", y.shape)print("Type of y: ", y.type())break

在这里插入图片描述

五、模型定义

1. 定义CNN模型

class CNNModel(nn.Module):def __init__(self):super(CNNModel, self).__init__()self.conv1 = nn.Conv3d(in_channels=1, out_channels=12, kernel_size=3, stride=1, padding=0)self.bn1 = nn.BatchNorm3d(12)self.conv2 = nn.Conv3d(in_channels=12, out_channels=12, kernel_size=3, stride=1, padding=0)self.bn2 = nn.BatchNorm3d(12)self.pool1 = nn.MaxPool3d(2, 2)self.conv4 = nn.Conv3d(in_channels=12, out_channels=24, kernel_size=3, stride=1, padding=0)self.bn4 = nn.BatchNorm3d(24)self.pool2 = nn.MaxPool3d(2, 2)self.conv5 = nn.Conv3d(in_channels=24, out_channels=24, kernel_size=3, stride=1, padding=0)self.bn5 = nn.BatchNorm3d(24)self.pool3 = nn.MaxPool3d(2, 2)self.fc1 = nn.Linear(24 * 26 * 26 * 26, 128)def forward(self, x):x = F.relu(self.bn1(self.conv1(x)))x = F.relu(self.bn2(self.conv2(x)))x = self.pool1(x)x = F.relu(self.bn4(self.conv4(x)))x = self.pool2(x)x = F.relu(self.bn5(self.conv5(x)))x = self.pool3(x)# print(x.shape)x = x.view(-1, 24 * 26 * 26 * 26)x = self.fc1(x)return xmodel = CNNModel().to(device)from torchinfo import summarysummary(model, (4, 1, 224, 224, 224))

在这里插入图片描述

2. 定义融合模型

class MultiModalNN(nn.Module):def __init__(self):super(MultiModalNN, self).__init__()self.cnn_model = CNNModel()  # 图像 CNNself.fc_numeric = nn.Linear(3, 64)  # 假设数值型数据有 3 个特征self.fc_combined = nn.Linear(128 + 64, 3)  # 最终分类或回归层def forward(self, mri_data, numeric_data):# 处理 MRI 数据img_features = self.cnn_model(mri_data)# 处理数值型数据num_features = F.relu(self.fc_numeric(numeric_data))# print(img_features.shape, num_features.shape)# 合并两种特征combined = torch.cat((img_features, num_features), dim=1)# 输出分类或回归结果output = self.fc_combined(combined)return output# 创建多模态模型
multi_modal_model = MultiModalNN().to(device)print(multi_modal_model)

在这里插入图片描述

3. 定义训练函数

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

4. 定义测试函数

def test(dataloader, model, loss_fn):size = len(dataloader.dataset)  # 测试集的大小,一共10000张图片num_batches = len(dataloader)   # 批次数目,313(10000/32=312.5,向上取整)test_loss, test_acc = 0, 0# 当不进行训练时,停止梯度更新,节省计算内存消耗with torch.no_grad():for X_1, X_2, y in dataloader:X_1, X_2, y = X_1.to(device), X_2.to(device), y.to(device)# 计算lossy_pred = model(X_1, X_2)loss   = loss_fn(y_pred, y)test_loss += loss.item()test_acc  += (y_pred.argmax(1) == y).type(torch.float).sum().item()test_acc  /= sizetest_loss /= num_batchesreturn test_acc, test_loss

六、训练模型

1. 设置超参数

loss_fn = nn.CrossEntropyLoss()  # 创建损失函数
learn_rate = 1e-3  # 学习率
opt = torch.optim.Adam(multi_modal_model.parameters(), lr=learn_rate)

2. 训练模型

epochs = 10
train_loss = []
train_acc = []
test_loss = []
test_acc = []for epoch in range(epochs):multi_modal_model.train()epoch_train_acc, epoch_train_loss = train(train_loader, multi_modal_model, loss_fn, opt)multi_modal_model.eval()epoch_test_acc, epoch_test_loss = test(test_loader, multi_modal_model, loss_fn)train_acc.append(epoch_train_acc)train_loss.append(epoch_train_loss)test_acc.append(epoch_test_acc)test_loss.append(epoch_test_loss)template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}')print(template.format(epoch + 1, epoch_train_acc * 100, epoch_train_loss, epoch_test_acc * 100, epoch_test_loss))
print('Done')

👉 代码输出:

Epoch: 1, Train_acc:34.1%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 2, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 3, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 4, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 5, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 6, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 7, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 8, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 9, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch:10, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Done

3. 结果分析

  1. 准确率(Accuracy)

    • 训练集和验证集的准确率都非常低且基本保持不变。
    • 这表明模型可能没有很好地学习到数据中的模式。
  2. 损失(Loss)

    • 训练损失和测试损失均为 nan(Not a Number),说明在计算损失时出现了数值不稳定的情况。

4. 可能的原因及解决方案

  1. 损失为 nan:通常是由于梯度爆炸或反向传播过程中出现了无效的数值操作(如除以零、对数运算等)。
  • 解决方案
    • 检查输入数据:确保所有输入数据都是有效的,并且没有异常值。
    • 初始化权重:适当的权重初始化可以避免梯度消失/爆炸问题。
    • 使用梯度裁剪:限制梯度的最大范数,防止梯度爆炸。
    • 调整学习率:过高的学习率可能导致梯度爆炸。尝试减小学习率。
    • 数据标准化:确保输入数据经过适当的标准化处理。
  1. 准确率较低且不变:模型可能过于简单无法捕捉数据中的复杂模式,或者存在过拟合的风险。
  • 解决方案
    • 增加模型复杂度:适当增加卷积层的数量或每层的输出通道数。
    • 正则化:添加 Dropout 层或其他正则化技术来减少过拟合。
    • 数据增强:通过数据增强技术提高模型的泛化能力。
import matplotlib.pyplot as plt
# 隐藏警告
import warnings
warnings.filterwarnings("ignore")  # 忽略警告信息
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
plt.rcParams['figure.dpi'] = 100  # 分辨率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 Validation 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 Validation Loss')
plt.show()

在这里插入图片描述

七、总结

本实验是基于多模态数据(MRI图像和数值特征)的AD检测模型,在运行原代码的过程中碰到较多问题。第一个是出现“RuntimeError: mat1 and mat2 must have the same dtype, but got Long and Float”的报错。

表示在进行矩阵乘法(如 torch.mm 或线性层)时,输入的数据类型不一致,一个是 Long 类型(整数类型),另一个是 Float 类型(浮点数)。这里主要是通过在多个步骤中添加了断言检查,随后出现第二个报错"AssertionError: X_2 must be float32"。

表明X_2 的数据类型不是 torch.float32,这是在训练数据的预处理或加载过程中出现的问题。随后返回检查数据类型时发现X_2 dtype: torch.int64,通过强制转换为folat32后问题似乎得到了解决,但更推荐的是在数据准备阶段处理以避免后续的问题:

# 确保 strnums 是 numpy.float32 类型
strnums = np.array(strnums, dtype=np.float32)

我的代码相比原代码主要是修改了三个地方:

class ToTensor3D:"""将 3D numpy.ndarray 转换为 PyTorch Tensor,并调整维度顺序为 (C, D, H, W)。"""def __call__(self, img):# 确保输入为 numpy.ndarrayif isinstance(img, np.ndarray):img = torch.from_numpy(img).float()# 调整维度顺序为 (C, D, H, W),这里我们假设只有一个通道return img.unsqueeze(0)  # 添加通道维度
# 计算适合医学图像的均值和标准差
# 使用统计量对张量进行标准化
mean = tensor.mean()
std = tensor.std()
normalized_tensor = Normalize3D(mean=[mean], std=[std])(tensor)return normalized_tensor
class CNNModel(nn.Module):def __init__(self):super(CNNModel, self).__init__()self.conv1 = nn.Conv3d(in_channels=1, out_channels=12, kernel_size=3, stride=1, padding=0)  # 输入通道数改为1self.bn1 = nn.BatchNorm3d(12)self.conv2 = nn.Conv3d(in_channels=12, out_channels=12, kernel_size=3, stride=1, padding=0)self.bn2 = nn.BatchNorm3d(12)self.pool1 = nn.MaxPool3d(2, 2)self.conv4 = nn.Conv3d(in_channels=12, out_channels=24, kernel_size=3, stride=1, padding=0)self.bn4 = nn.BatchNorm3d(24)self.pool2 = nn.MaxPool3d(2, 2)self.conv5 = nn.Conv3d(in_channels=24, out_channels=24, kernel_size=3, stride=1, padding=0)self.bn5 = nn.BatchNorm3d(24)self.pool3 = nn.MaxPool3d(2, 2)self.fc1 = nn.Linear(24 * 26 * 26 * 26, 128)

最终成功跑通模型,期间还因为别的软件开太多内存不够崩了一次,但是结果却并不理想。尝试了下列操作结果反而变差了,另外两处改进:全局标准化因为内存不足没法子,减小目标尺寸到64x64x64出现报错……

  • 学习率调整:从1e-3 减小到 1e-4
  • 梯度裁剪:在反向传播后添加梯度裁剪,防止梯度爆炸
  • 权重初始化:使用 He 初始化 (kaiming_normal_) 和 Xavier 初始化 (xavier_uniform_) 来初始化卷积层和线性层的权重

Anyway,对菜鸡来说跑通就是胜利吧,实验到此告一段落,我得去修返回的文章了=.=

1. 主要深度学习方法

  • 卷积神经网络 (CNN): 用于处理MRI图像数据。通过一系列的3D卷积层 (nn.Conv3d)、批归一化层 (nn.BatchNorm3d) 和池化层 (nn.MaxPool3d) 来提取图像特征。
  • 全连接神经网络 (FCN): 用于处理数值型数据(年龄、性别等),并将其与CNN提取的图像特征进行融合,最终通过全连接层进行分类。

2. 多模态融合方式

  • 特征级融合:
    • 首先,使用CNN模型对MRI图像数据进行特征提取,得到图像特征向量 img_features
    • 然后,使用一个全连接层 self.fc_numeric 对数值型数据进行处理,得到数值特征向量 num_features
    • 最后,将这两个特征向量在维度1上进行拼接 (torch.cat((img_features, num_features), dim=1)),并通过另一个全连接层 self.fc_combined 进行最终的分类。

3. 难点及解决方案

  • 数据处理和标准化:
    • 难点: MRI图像数据通常具有不同的尺寸和强度范围,需要进行标准化处理。
    • 解决方案: 使用 interpolate_volume 函数将图像数据插值到统一大小,通过计算每个图像自身的均值和标准差进行标准化处理。
  • 模型设计和调参:
    • 难点: 设计合适的CNN结构以及确定全连接层的参数,以有效地融合多模态数据并进行准确分类。
    • 解决方案: 通过实验和调整卷积层、池化层以及全连接层的参数,同时使用交叉熵损失函数 (nn.CrossEntropyLoss) 和Adam优化器 (torch.optim.Adam) 进行训练。

4. 改进方向

  • 数据增强: 对MRI图像进行更多的数据增强操作,如旋转、翻转、缩放等,以增加数据的多样性,提高模型的泛化能力。
  • 模型优化:
    • 尝试更深层次的CNN结构,如3D ResNet,以更好地提取图像特征。
    • 调整全连接层的结构和参数,优化多模态特征融合的方式。
  • 超参数调优: 使用更系统的超参数调优方法,如随机搜索、网格搜索或更高级的调优算法(如Optuna),找到最优的超参数组合。
  • 集成学习: 将多个不同的多模态模型进行集成,以提高模型的稳定性和准确性。
  • 使用预训练模型: 在大规模医学图像数据集上预训练的模型,迁移学习到该任务中,以加速收敛并提高性能。

相关文章:

多模态融合:阿尔茨海默病检测

🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 一、实验介绍 本实验包含 645 名阿尔茨海默病受试者,分为 AD、CN 和 MCI 组,数据集包含 3D MRI 图像与一份CSV数据,MRI数据…...

Ceph 手动部署(CentOS9)

#Ceph手动部署、CentOS9、squid版本、数字版本19.2.0 #部署服务:块、对象、文件 一、部署前规划 1、兼容性确认 2、资源规划 节点类型节点名称操作系统CPU/内存硬盘网络组件安装集群节点CephAdm01CentOS94U/8GOS:40G,OSD:2*100GIP1:192.169.0.9(管理&集群),IP2:…...

家政预约小程序05活动管理

目录 1 搭建活动管理页面2 搭建活动规则页面3 搭建规则新增页面3 配置规则跳转4 搭建活动参与记录总结 上一篇我们介绍了活动管理的表结构设计,本篇我们介绍一下后台功能。 1 搭建活动管理页面 我们一共搭建了三个表,先搭建主表的后台功能。打开我们的后…...

解决安装pynini和WeTextProcessing报错问题

点击这里,访问博客 0. 背景 最近在给别人有偿部署ASR-LLM-TTS项目时遇到安装pynini和WeTextProcessing依赖报错的问题,报错信息如下: IC:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt" "-IC:\Program Files…...

【PCIe 总线及设备入门学习专栏 4.1 -- PCI 总线的地址空间分配】

文章目录 Overview 本文转自:https://blog.chinaaet.com/justlxy/p/5100053219 Overview PCI 总线具有32位数据/地址复用总线,所以其存储地址空间为 2324GB。也就是PCI上的所有设备共同映射到这4GB上,每个PCI设备占用唯一的一段PCI地址&…...

华为配置 之 RIP

简介: RIP(路由信息协议)是一种广泛使用的内部网关协议,基于距离向量算法来决定路径。它通过向全网广播路由控制信息来动态交换网络拓扑信息,从而计算出最佳路由路径。RIP易于配置和理解,非常适用于小型网络…...

探寻AI Agent:开启知识图谱自动生成新篇章(17/30)

一、AI Agent 与知识图谱:智能时代的双雄 在当今科技飞速发展的时代,人工智能如同一股汹涌澎湃的浪潮,正以前所未有的力量重塑着我们的世界。而在这股浪潮中,AI Agent 与知识图谱无疑是两颗最为璀璨的明珠,它们各自发挥…...

卸载wps后word图标没有变成白纸恢复

这几天下载了个wps教育版,后头用完了删了 用习惯的2019图标 给兄弟我干没了??? 其他老哥说什么卸载关联重新下 ,而且还要什么撤销保存原来的备份什么,兄弟也是不得不怂了 后头就发现了这个半宝藏博主&…...

LeetCode 热题 100_二叉树的直径(40_543_简单_C++)(二叉树;递归)

LeetCode 热题 100_二叉树的直径(40_543) 题目描述:输入输出样例:题解:解题思路:思路一(递归): 代码实现代码实现(思路一(递归)&#…...

【数据结构】线性数据结构——链表

1. 定义 链表是一种线性数据结构,由多个节点(Node)组成。每个节点存储数据和指向下一个节点的指针。与数组不同,链表的节点不需要在内存中连续存储。 2. 特点 动态存储: 链表的大小不固定,可以动态增加或…...

开源存储详解-分布式存储与ceph

ceph体系结构 rados:reliable, autonomous, distributed object storage, rados rados采用c开发 对象存储 ceph严格意义讲只提供对象存储能力,ceph的块存储能力实际是基于对象存储库librados的rbd 对象存储特点 对象存储采用put/get/delete&#xf…...

[算法] [leetcode-509] 斐波那契数

509 斐波那契数 斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) 0,F(1) 1 F(n) F(n - 1) F(n - 2),其中 n…...

运维人员的Go语言学习路线

以下是一份更为详细的适合运维人员的Go语言学习路线图: 一、基础环境搭建与入门(第 1 - 2 周) 第 1 周 环境搭建 在本地开发机和常用的运维服务器环境(如 Linux 系统)中安装 Go 语言。从官方网站(https://…...

[创业之路-222]:波士顿矩阵与GE矩阵在业务组合选中作用、优缺点比较

目录 一、波士顿矩阵 1、基本原理 2、各象限产品的定义及战略对策 3、应用 4、优点与局限性 二、技术成熟度模型与产品生命周期模型的配对 1、技术成熟度模型 2、产品生命周期模型 3、技术成熟度模型与产品生命周期模型的配对 三、产品生命周期与产品类型的对应关系 …...

安卓入门十一 常用网络协议四

MQTT(Message Queuing Telemetry Transport) MQTT是一种轻量级的、发布/订阅模式的消息传输协议。它被设计用于在低带宽或不稳定网络环境下,实现物联网设备之间的可靠通信。 4.1 MQTT详细介绍 发布/订阅模式:MQTT 使用发布/订…...

《机器学习》——利用OpenCV库中的KNN算法进行图像识别

文章目录 KNN算法介绍下载OpenCV库实验内容实验结果完整代码手写数字传入模型训练 KNN算法介绍 一、KNN算法的基本要素 K值的选择:K值代表选择与新测试样本距离最近的前K个训练样本数,通常K是不大于20的整数。K值的选择对算法结果有重要影响&#xff0c…...

StarRocks 存算分离在得物的降本增效实践

编者荐语: 得物优化数据引擎布局,近期将 4000 核 ClickHouse 迁移至自建 StarRocks,成本降低 40%,查询耗时减半,集群稳定性显著提升。本文详解迁移实践与成果,文末附丁凯剑老师 StarRocks Summit Asia 2024…...

Tube Qualify弯管测量系统在汽车管路三维检测中的应用

从使用量上来说,汽车行业是使用弯管零件数量最大的单一行业。在汽车的燃油,空调,排气,转向,制动等系统中都少不了管路。汽车管件形状复杂,且由于安装空间限制,汽车管件拥有不同弯曲半径&#xf…...

udp分片报文发送和接收

读文件通过udp分片发送的目的端:(包含错误的分片包) #!/usr/bin/python # -*- coding: utf-8 -*-#python send_100frag_file.py -p 55432 -f snatdownloadimport argparse import loggingfrom scapy.all import *# Define the maximum size …...

【从零开始入门unity游戏开发之——C#篇39】C#反射使用——Type 类、Assembly 类、Activator 类操作程序集

文章目录 前言一、前置知识1、编译器2、程序集(Assembly)3、元数据(Metadata) 二、反射1、反射的概念2、反射的作用3、反射的核心Type 类3.1 Type 类介绍3.2 不同方法获取 Type3.3 获取type类型所在的程序集的相关信息 4、反射的常…...

安卓触摸事件的传递

setOnTouchListener()返回值的副作用(触摸事件是否继续往下或往后传递)如下: 返回值效果是否往下层view传递是否往当前view的后续监听传递true该pointer离开屏幕前的后续所有触摸事件都会传递给该TouchListener否否false该pointer离开屏幕前…...

idea项目导入gitee 码云

1、安装gitee插件 IDEA 码云插件已由 gitosc 更名为 gitee。 1 在码云平台帮助文档http://git.mydoc.io/?t153739上介绍的很清楚,推荐前两种方法, 搜索码云插件的时候记得名字是gitee,gitosc已经搜不到了。 2、使用码云托管项目 如果之…...

典型常见的基于知识蒸馏的目标检测方法总结三

来源:Google学术2023-2024的顶会顶刊论文 NeurIPS 2022:Towards Efficient 3D Object Detection with Knowledge Distillation 为3D目标检测提出了一种知识蒸馏的Benchmark范式,包含feature的KD,Logit的cls和reg的KD&#xff0c…...

端口被占用

端口8080被占用 哈哈哈,我是因为后端项目跑错了,两个项目后端名称太像了; (1)netstat -aon | findstr 8080,找到占用8080端口的进程号,获取对应的进程号pid; (2&#…...

Javascript知识框架图(待完善)

以下是一个清晰且详细的 JavaScript 知识框架,涵盖基础知识到高级概念,适合学习和参考: JavaScript 知识框架 1. 基础知识 数据类型 原始类型:Number,String,Boolean,Null,Undefin…...

清华大学Python包镜像站点

清华大学提供了一个Python包镜像站点,其中包括了许多常用的Python包。使用这个镜像站点可以提高下载Python包时的速度,因为包已经存储在国内的服务器上,从而减少了网络延迟。 要使用清华的pip镜像,你可以在pip命令中指定-i参数来…...

逆境清醒文章总目录表

逆境清醒文章总目录表 零、时光宝盒🌻 (https://blog.csdn.net/weixin_69553582 逆境清醒) 《你的答案》歌曲原唱:阿冗,填 词:林晨阳、刘涛,谱曲:刘涛 也许世界就这样&#xff0c…...

LeetCode算法题——移除元素

题目描述 给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。 假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作&#xff1…...

常见的中间件漏洞

1.Tomcat Tomcat介绍 tomcat是⼀个开源而且免费的jsp服务器,默认端口 : 8080,属于轻量级应⽤服务器。它可以实现 JavaWeb程序的装载,是配置JSP(Java Server Page)和JAVA系统必备的⼀款环境。 在历史上也披露出来了很…...

IPv6的过度技术

如何界定手动与自动?  主要是隧道目标地址能否自动获取 👯1. 双栈 必须支持IPv4和IPv6协议  链接双栈网络的接口必须同时配置v4和v6地址  路由器能够根据二层标记识别协议,type:0x0800代表IPV4,type:0x…...