0307data quality reproduce_1 uname -m终端查看mac芯片目录:main代码import numpy as np import scipy.io as sio from sklearn.decomposition import PCA from sklearn.model_selection import train_test_split from sklearn.metrics import confusion_matrix, accuracy_score, classification_report, cohen_kappa_score import torch import torch.nn as nn import torch.optim as optim from operator import truediv import get_cls_map import time import SSFTTnet def loadData(): # 读入数据 data = sio.loadmat('../data/Indian_pines_corrected.mat')['indian_pines_corrected'] labels = sio.loadmat('../data/Indian_pines_gt.mat')['indian_pines_gt'] return data, labels # 对高光谱数据 X 应用 PCA 变换 def applyPCA(X, numComponents): newX = np.reshape(X, (-1, X.shape[2])) pca = PCA(n_components=numComponents, whiten=True) newX = pca.fit_transform(newX) newX = np.reshape(newX, (X.shape[0], X.shape[1], numComponents)) return newX # 对单个像素周围提取 patch 时,边缘像素就无法取了,因此,给这部分像素进行 padding 操作 def padWithZeros(X, margin=2): newX = np.zeros((X.shape[0] + 2 * margin, X.shape[1] + 2* margin, X.shape[2])) x_offset = margin y_offset = margin newX[x_offset:X.shape[0] + x_offset, y_offset:X.shape[1] + y_offset, :] = X return newX # 在每个像素周围提取 patch ,然后创建成符合 keras 处理的格式 def createImageCubes(X, y, windowSize=5, removeZeroLabels = True): # 给 X 做 padding margin = int((windowSize - 1) / 2) zeroPaddedX = padWithZeros(X, margin=margin) # split patches patchesData = np.zeros((X.shape[0] * X.shape[1], windowSize, windowSize, X.shape[2])) patchesLabels = np.zeros((X.shape[0] * X.shape[1])) patchIndex = 0 for r in range(margin, zeroPaddedX.shape[0] - margin): for c in range(margin, zeroPaddedX.shape[1] - margin): patch = zeroPaddedX[r - margin:r + margin + 1, c - margin:c + margin + 1] patchesData[patchIndex, :, :, :] = patch patchesLabels[patchIndex] = y[r-margin, c-margin] patchIndex = patchIndex + 1 if removeZeroLabels: patchesData = patchesData[patchesLabels0,:,:,:] patchesLabels = patchesLabels[patchesLabels0] patchesLabels -= 1 return patchesData, patchesLabels def splitTrainTestSet(X, y, testRatio, randomState=345): X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=testRatio, random_state=randomState, stratify=y) return X_train, X_test, y_train, y_test BATCH_SIZE_TRAIN = 64 def create_data_loader(): # 地物类别 # class_num = 16 # 读入数据 X, y = loadData() # 用于测试样本的比例 test_ratio = 0.90 # 每个像素周围提取 patch 的尺寸 patch_size = 13 # 使用 PCA 降维,得到主成分的数量 pca_components = 30 print('Hyperspectral data shape: ', X.shape) print('Label shape: ', y.shape) print('\n... ... PCA tranformation ... ...') X_pca = applyPCA(X, numComponents=pca_components) print('Data shape after PCA: ', X_pca.shape) print('\n... ... create data cubes ... ...') X_pca, y_all = createImageCubes(X_pca, y, windowSize=patch_size) print('Data cube X shape: ', X_pca.shape) print('Data cube y shape: ', y.shape) print('\n... ... create train test data ... ...') Xtrain, Xtest, ytrain, ytest = splitTrainTestSet(X_pca, y_all, test_ratio) print('Xtrain shape: ', Xtrain.shape) print('Xtest shape: ', Xtest.shape) # 改变 Xtrain, Ytrain 的形状,以符合 keras 的要求 X = X_pca.reshape(-1, patch_size, patch_size, pca_components, 1) Xtrain = Xtrain.reshape(-1, patch_size, patch_size, pca_components, 1) Xtest = Xtest.reshape(-1, patch_size, patch_size, pca_components, 1) print('before transpose: Xtrain shape: ', Xtrain.shape) print('before transpose: Xtest shape: ', Xtest.shape) # 为了适应 pytorch 结构,数据要做 transpose X = X.transpose(0, 4, 3, 1, 2) Xtrain = Xtrain.transpose(0, 4, 3, 1, 2) Xtest = Xtest.transpose(0, 4, 3, 1, 2) print('after transpose: Xtrain shape: ', Xtrain.shape) print('after transpose: Xtest shape: ', Xtest.shape) # 创建train_loader和 test_loader X = TestDS(X, y_all) trainset = TrainDS(Xtrain, ytrain) testset = TestDS(Xtest, ytest) train_loader = torch.utils.data.DataLoader(dataset=trainset, batch_size=BATCH_SIZE_TRAIN, shuffle=True, num_workers=0, ) test_loader = torch.utils.data.DataLoader(dataset=testset, batch_size=BATCH_SIZE_TRAIN, shuffle=False, num_workers=0, ) all_data_loader = torch.utils.data.DataLoader(dataset=X, batch_size=BATCH_SIZE_TRAIN, shuffle=False, num_workers=0, ) return train_loader, test_loader, all_data_loader, y """ Training dataset""" class TrainDS(torch.utils.data.Dataset): def __init__(self, Xtrain, ytrain): self.len = Xtrain.shape[0] self.x_data = torch.FloatTensor(Xtrain) self.y_data = torch.LongTensor(ytrain) def __getitem__(self, index): # 根据索引返回数据和对应的标签 return self.x_data[index], self.y_data[index] def __len__(self): # 返回文件数据的数目 return self.len """ Testing dataset""" class TestDS(torch.utils.data.Dataset): def __init__(self, Xtest, ytest): self.len = Xtest.shape[0] self.x_data = torch.FloatTensor(Xtest) self.y_data = torch.LongTensor(ytest) def __getitem__(self, index): # 根据索引返回数据和对应的标签 return self.x_data[index], self.y_data[index] def __len__(self): # 返回文件数据的数目 return self.len def train(train_loader, epochs): # 使用GPU训练,可以在菜单 "代码执行工具" - "更改运行时类型" 里进行设置 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # 网络放到GPU上 net = SSFTTnet.SSFTTnet().to(device) # 交叉熵损失函数 criterion = nn.CrossEntropyLoss() # 初始化优化器 optimizer = optim.Adam(net.parameters(), lr=0.001) # 开始训练 total_loss = 0 for epoch in range(epochs): net.train() for i, (data, target) in enumerate(train_loader): data, target = data.to(device), target.to(device) # 正向传播 + 反向传播 + 优化 # 通过输入得到预测的输出 outputs = net(data) # 计算损失函数 loss = criterion(outputs, target) # 优化器梯度归零 optimizer.zero_grad() # 反向传播 loss.backward() optimizer.step() total_loss += loss.item() print('[Epoch: %d] [loss avg: %.4f] [current loss: %.4f]' % (epoch + 1, total_loss / (epoch + 1), loss.item())) print('Finished Training') return net, device def test(device, net, test_loader): count = 0 # 模型测试 net.eval() y_pred_test = 0 (ssftt) liuxinyu@liuxinyus-MacBook-Pro HSI_SSFTT-main % 很好,问题已经看出来了。 这个脚本里有一处非常关键的路径设置: data = sio.loadmat('../data/Indian_pines_corrected.mat') labels = sio.loadmat('../data/Indian_pines_gt.mat') 这意味着: 你不能在项目根目录 HSI_SSFTT-main 直接运行它。 因为如果你当前目录是: ~/research/HSI_SSFTT-main 那么 ../data 会被解释成: ~/research/data 这是错的。 正确运行方式 你要先进入 cls_SSFTT_IP 文件夹,再运行脚本。 在终端执行: cd ~/research/HSI_SSFTT-main/cls_SSFTT_IP python IP_train.py 为什么要这样做 因为此时当前目录变成: ~/research/HSI_SSFTT-main/cls_SSFTT_IP 那么脚本里的: '../data/Indian_pines_corrected.mat' 就会正确指向: ~/research/HSI_SSFTT-main/data/Indian_pines_corrected.mat 另外一个你已经提前避开的坑 这个脚本写的是: import get_cls_map import SSFTTnet 这也更适合你在 cls_SSFTT_IP 目录里运行,因为这两个 .py 文件就在当前目录下。 你现在直接这样做 cd ~/research/HSI_SSFTT-main/cls_SSFTT_IP python IP_train.py 把输出发我。 我先帮你预判一下接下来可能出现的情况 情况 1:直接开始训练 你会看到类似: Hyperspectral data shape PCA transformation create data cubes Xtrain shape / Xtest shape Epoch ... loss ... 这说明已经跑通了。 情况 2:缺包 可能报: ModuleNotFoundError: No module named 'x' 你把报错发我,我帮你补装。 情况 3:模型/维度报错 那就是代码层面的兼容问题,我会继续帮你改。 顺便提醒你一点 脚本现在设备选择是: device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") 在你的 Mac M4 上,一般不会走 CUDA,而是走: cpu 这是正常的。先跑通就行,后面我们再考虑要不要改成 mps。 先把这条运行结果发我: cd ~/research/HSI_SSFTT-main/cls_SSFTT_IP python IP_train.py 这些代码是主程序吗 是的,你看到的 IP_train.py 基本就是这个项目的主程序(main training script)。我帮你把整个项目结构讲清楚,这样你以后改代码就不会迷路了。 1️⃣ 这个项目的整体结构 你的目录是: HSI_SSFTT-main │ ├── README.md ├── data │ ├── Indian_pines_corrected.mat │ └── Indian_pines_gt.mat │ └── cls_SSFTT_IP ├── IP_train.py ⭐ 主程序(训练+测试) ├── SSFTTnet.py ⭐ Transformer模型定义 ├── get_cls_map.py ⭐ 生成分类图 ├── classification_maps ├── cls_params └── cls_result 2️⃣ 各个文件是干什么的 ⭐ IP_train.py (主程序) 这个文件负责: 1️⃣ 读取数据 loadData() 读取: Indian_pines_corrected.mat Indian_pines_gt.mat 2️⃣ PCA降维 applyPCA() 原始高光谱数据: 145 × 145 × 200 降到: 145 × 145 × 30 3️⃣ 构造 patch createImageCubes() 把每个像素周围 13×13 patch 提取出来。 4️⃣ 划分训练测试集 splitTrainTestSet() 5️⃣ 创建 PyTorch DataLoader create_data_loader() 6️⃣ 调用 Transformer 网络 net = SSFTTnet.SSFTTnet() 7️⃣ 训练模型 train() 8️⃣ 测试模型 test() 3️⃣ Transformer模型在哪里 在这里: SSFTTnet.py 这个文件里定义了: class SSFTTnet(nn.Module) 这里面就是: Spectral-Spatial Feature Tokenization Transformer 也就是论文核心。 4️⃣ 生成分类图 训练完成后: get_cls_map.py 会把预测结果生成: classification_maps 类似这种图: Indian Pines classification map 5️⃣ 代码运行流程(完整) 当你运行: python IP_train.py 流程是: 读取数据 ↓ PCA降维 ↓ 提取patch ↓ train/test split ↓ DataLoader ↓ SSFTT Transformer ↓ 训练 ↓ 测试 ↓ 生成分类图 6️⃣ 你现在的目标 我们只需要 成功运行这个脚本: IP_train.py 所以在终端运行: cd ~/research/HSI_SSFTT-main/cls_SSFTT_IP python IP_train.py 7️⃣ 运行成功后会看到类似输出 例如: Hyperspectral data shape: (145,145,200) Label shape: (145,145) PCA transformation... Data shape after PCA: (145,145,30) create data cubes... Xtrain shape: ... Xtest shape: ... Epoch 1 loss ... Epoch 2 loss ... ... 8️⃣ 下一步(很重要) 等你跑起来以后,我会带你做三件很关键的事情: 1️⃣ 解释这个 Transformer 架构 (SSFTT 是怎么做 tokenization 的) 2️⃣ 画出网络结构图 (类似你之前给我的那种) 3️⃣ 改造成你的 wave 任务 Radar wave sequence ↓ Transformer ↓ Gauge wave sequence 也就是: radar → gauge 校正网络 你现在运行: cd ~/research/HSI_SSFTT-main/cls_SSFTT_IP python IP_train.py 把 终端输出发我。 我们一起看它是不是已经开始训练了。 import numpy as np啥意思 很好,这个问题其实是 Python 基础语法 + 科研代码最常见的第一行。我给你解释得非常清楚。 1️⃣ import numpy as np 是什么意思 import numpy as np 意思是: 导入 Python 的 NumPy 数值计算库,并把它简称为 np。 2️⃣ 为什么要用 NumPy NumPy 是 科学计算最重要的 Python 库之一,主要用来: 矩阵运算 向量运算 数组处理 线性代数 数值计算 几乎所有: 机器学习 深度学习 科学计算 信号处理 都会用到它。 3️⃣ as np 是干什么的 如果只写: import numpy 使用时要写: numpy.array([1,2,3]) 很长。 所以大家都会写: import numpy as np 以后只要写: np.array([1,2,3]) 这是一种 全世界 Python 科研代码的约定写法。 4️⃣ 举个简单例子 import numpy as np a = np.array([1,2,3]) b = np.array([4,5,6]) c = a + b print(c) 输出: [5 7 9] 5️⃣ 在你这个项目里 NumPy干什么 在 IP_train.py 里: import numpy as np 主要用来做: ① 创建数据数组 patchesData = np.zeros(...) 生成一个 全零矩阵。 ② reshape 数据 newX = np.reshape(X, (-1, X.shape[2])) 改变数组形状。 ③ PCA前的数据处理 X_pca = applyPCA(X) PCA输入输出都是 NumPy 数组。 6️⃣ 在深度学习里的关系 数据流程一般是: NumPy array ↓ Tensor (PyTorch) ↓ Neural Network 在代码里就是: torch.FloatTensor(Xtrain) 把 NumPy 转成 PyTorch tensor。 7️⃣ 你这段代码整体的逻辑 例如: Xtrain = Xtrain.reshape(...) Xtrain = Xtrain.transpose(...) self.x_data = torch.FloatTensor(Xtrain) 意思是: NumPy 数据 ↓ reshape ↓ transpose ↓ 转成 torch tensor ↓ 输入 Transformer 8️⃣ 总结一句话 import numpy as np 就是: 导入 NumPy 数值计算库,并把名字缩写成 np。 如果你愿意,我可以 把 IP_train.py 整个代码逐行讲一遍,包括: PCA patch extraction DataLoader Transformer 输入结构 这样你就能 完全读懂这个项目代码。 数据流程为什么要建立array,再转换为tensor? 这是一个非常核心的问题,几乎所有深度学习代码都会经历这一步。理解这个,你就真正理解了 Python科学计算 → 深度学习框架 的关系。 我给你从 工程 + 数学 + 历史原因 三个层面解释。 一、数据为什么先是 NumPy array? 因为 Python科学计算的标准数据结构就是 NumPy array。 例如: import numpy as np 在科研中: MATLAB → matrix Python → NumPy array 几乎所有科学计算库都用 NumPy: scipy scikit-learn PCA signal processing FFT image processing 例如你代码里的: pca = PCA(n_components=numComponents) newX = pca.fit_transform(newX) scikit-learn 的 PCA 输入必须是 NumPy array。 所以数据流程是: .mat 数据 ↓ NumPy array ↓ PCA ↓ patch extraction 二、为什么深度学习要 Tensor? 因为 GPU计算需要 Tensor,而不是 NumPy array。 PyTorch / TensorFlow 都使用 Tensor(张量)。 Tensor 的特点: 1️⃣ 可以放到 GPU tensor.to(device) 2️⃣ 支持自动求导 loss.backward() 3️⃣ 支持深度学习运算图 4️⃣ 支持 CUDA / MPS / TPU 三、NumPy array vs Tensor 类型 作用 NumPy array 科学计算 Tensor 深度学习 举例: NumPy a = np.array([1,2,3]) 只能在 CPU 上算。 Tensor a = torch.tensor([1,2,3]) 可以: a.to("cuda") 在 GPU 上计算。 四、为什么代码先 NumPy 再 Tensor? 因为数据处理库都是 NumPy。 你代码里的流程: X, y = loadData() 读取 .mat: data = sio.loadmat(...) 得到: NumPy array 然后 PCA: X_pca = applyPCA(X) 还是 NumPy。 然后 patch extraction: createImageCubes() 还是 NumPy。 最后才转换为 Tensor: self.x_data = torch.FloatTensor(Xtrain) 变成: Tensor 送进网络: outputs = net(data) 五、整个数据流程(你的项目) 真实流程是: .mat hyperspectral data ↓ NumPy array ↓ PCA ↓ patch extraction ↓ train/test split ↓ NumPy array ↓ torch.FloatTensor ↓ Tensor ↓ Transformer ↓ prediction 六、为什么不一开始就 Tensor? 因为很多库 只支持 NumPy: 例如: from sklearn.decomposition import PCA sklearn 不能直接处理 Tensor。 七、NumPy 和 Tensor 可以互相转换 NumPy → Tensor torch.from_numpy(x) Tensor → NumPy x.numpy() 八、在你未来的 radar-wave 项目里也是一样 例如: Radar time series ↓ NumPy ↓ filter / FFT / QC ↓ NumPy ↓ Tensor ↓ Transformer ↓ Gauge wave 九、一个非常重要的理解 NumPy 是: 科学计算语言 Tensor 是: 深度学习计算图 十、工程上的黄金规则 科研代码通常遵循: Data processing → NumPy Deep learning → Tensor 如果你愿意,我可以下一步 给你画出这个 SSFTT Transformer 的完整数据流图: 从 145×145×200 hyperspectral cube 到 Transformer token 再到 classification output 这样你会完全理解 这个论文的核心架构。 scipy scikit-learn PCA分别是什么科学计算库 这是一个很好的问题 👍 不过这里有一个小误解:PCA 不是库(library),而是一个算法。 我给你把这三个概念清楚地区分一下。 一、SciPy SciPy 是 Python 的科学计算库。 它建立在 NumPy 之上,提供很多高级数学工具,例如: 线性代数 优化 信号处理 插值 统计 微分方程 例如你的代码里: import scipy.io as sio 用的是 SciPy 里的 io 模块,作用是: 读取 MATLAB 的 .mat 文件 例如: data = sio.loadmat('Indian_pines_corrected.mat') 二、scikit-learn scikit-learn 是 Python 的经典机器学习库。 它主要用于: 机器学习算法 数据预处理 特征工程 模型评估 常见算法包括: PCA SVM Random Forest KNN Logistic Regression clustering 在你的代码里: from sklearn.decomposition import PCA 说明: PCA 算法来自 scikit-learn 三、Principal Component Analysis PCA(Principal Component Analysis)是一个数学算法。 中文叫: 主成分分析 主要作用是: 降维 举例(你的高光谱数据) 原始数据: 145 × 145 × 200 含义: 空间:145×145 像素 光谱:200 个波段 200维非常高。 PCA 会把: 200维 → 30维 保留大部分信息。 你的代码就是: pca_components = 30 X_pca = applyPCA(X, numComponents=pca_components) 变成: 145 × 145 × 30 四、三者关系 关系其实是: NumPy ↑ SciPy ↑ scikit-learn ↑ PCA算法 或者更清晰一点: NumPy ↓ SciPy (科学计算) ↓ scikit-learn (机器学习) ↓ PCA (具体算法) 五、你的代码中它们分别做什么 在 IP_train.py 里: ① SciPy import scipy.io as sio 作用: 读取 .mat 数据 ② scikit-learn from sklearn.decomposition import PCA 作用: 降维 ③ PCA 在代码里: pca = PCA(n_components=30) newX = pca.fit_transform(newX) 作用: 200维光谱 → 30维特征 六、在你的研究里其实经常用 在你做的: radar wave / gauge wave 也可能用 PCA,例如: 原始信号特征 ↓ PCA降维 ↓ 机器学习模型 或者: wave spectrum features ↓ PCA ↓ neural network 如果你愿意,我可以 用图把你这段代码的完整数据流程画出来: .mat hyperspectral data ↓ SciPy读取 ↓ NumPy数组 ↓ PCA降维 ↓ patch extraction ↓ Tensor ↓ Transformer 这样你会更容易理解这个项目的整体逻辑。 data = sio.loadmat('Indian_pines_corrected.mat')语法 参数 作用/功能 很好,这一行代码其实包含 Python语法 + SciPy函数调用 + MATLAB数据读取 三个层面的知识。我们逐层解释。 一、完整代码 data = sio.loadmat('Indian_pines_corrected.mat') 可以拆成四部分: data = sio.loadmat ( 'Indian_pines_corrected.mat' ) 变量名 函数 参数 二、语法结构 这是 Python 最常见的 函数调用 + 赋值语句。 结构是: 变量 = 函数(参数) 意思是: 调用函数 → 得到结果 → 存入变量 所以这句话的意思是: 调用 loadmat 函数读取 .mat 文件 然后把读取结果存到 data 变量里 三、sio 是什么 前面代码有: import scipy.io as sio 意思是: 导入 SciPy 的 io 模块 并取别名 sio 所以: sio.loadmat 实际上是: scipy.io.loadmat 四、loadmat() 是什么函数 loadmat() 是 SciPy 里的函数,用来: 读取 MATLAB 的 .mat 文件 MATLAB保存数据: .mat Python通过 SciPy 读取。 五、参数是什么 函数调用: sio.loadmat('Indian_pines_corrected.mat') 参数是: 'Indian_pines_corrected.mat' 这是: 文件路径 表示要读取的文件。 六、返回值是什么 loadmat() 返回的是: Python dictionary(字典) 类似: { '__header__': ..., '__version__': ..., '__globals__': ..., 'indian_pines_corrected': ndarray(...) } 所以: data = sio.loadmat(...) 此时: data 其实是: 一个字典 七、为什么后面还要取变量 你的代码里其实是这样写的: data = sio.loadmat('../data/Indian_pines_corrected.mat')['indian_pines_corrected'] 注意最后这一部分: ['indian_pines_corrected'] 意思是: 从字典里取出 key = 'indian_pines_corrected' 所以最终: data 就变成: NumPy array 八、完整过程 MATLAB 文件结构: Indian_pines_corrected.mat ↓ { 'indian_pines_corrected': matrix } Python读取: mat = sio.loadmat('Indian_pines_corrected.mat') 得到: { 'indian_pines_corrected': array(...) } 然后: data = mat['indian_pines_corrected'] 最终: data = NumPy array 九、这个数据是什么 在这个项目里: Indian Pines hyperspectral dataset 形状是: 145 × 145 × 200 意思是: 145 × 145 像素 200 个光谱波段 十、你的代码中的作用 这一行: data = sio.loadmat('../data/Indian_pines_corrected.mat')['indian_pines_corrected'] 作用是: 读取高光谱数据 返回: NumPy array 然后进入后面的流程: load data ↓ PCA ↓ patch extraction ↓ train/test split ↓ Transformer 十一、一句话总结 data = sio.loadmat('Indian_pines_corrected.mat') 作用: 读取 MATLAB 的 .mat 文件, 并返回一个 Python 字典对象。 如果你愿意,我可以 把 IP_train.py 逐行解释(像读论文一样),包括: PCA patch extraction DataLoader Transformer输入结构 这样你就能 完全读懂这个项目代码。 ['indian_pines_corrected']这里为什么是中括号,而不是小括号?在函数调用的过程中中括号和小括号有什么含义上的区分?你怎么知道['indian_pines_corrected']代表着从字典里取出 key = 'indian_pines_corrected'?为什么从字典里取出key = 'indian_pines_corrected'后得到的data不是字典而是array? 这些问题非常好,本质是在问 Python 三种括号的语义差别 + 字典索引机制 + .mat 文件结构。我一步一步讲清楚。 一、为什么是 [] 而不是 () 看这行代码: data = sio.loadmat('../data/Indian_pines_corrected.mat')['indian_pines_corrected'] 这里其实有 两步操作: tmp = sio.loadmat('../data/Indian_pines_corrected.mat') data = tmp['indian_pines_corrected'] 1️⃣ 小括号 () 表示: 函数调用 例如: sio.loadmat('file.mat') 含义: 调用 loadmat 函数 2️⃣ 中括号 [] 表示: 索引 / 取值 常见用于: 数据类型 用法 list list[0] numpy array array[0] dictionary dict['key'] 所以: tmp['indian_pines_corrected'] 含义是: 从字典 tmp 中取 key='indian_pines_corrected' 的 value 二、为什么我知道 loadmat 返回字典 因为 SciPy 官方文档定义就是这样。 函数: scipy.io.loadmat() 返回类型: dict 结构类似: { '__header__': 'MATLAB 5.0 MAT-file', '__version__': '1.0', '__globals__': [], 'indian_pines_corrected': ndarray(...) } 所以: tmp = sio.loadmat('Indian_pines_corrected.mat') 得到: tmp = { '__header__': ..., '__version__': ..., '__globals__': ..., 'indian_pines_corrected': numpy_array } 三、为什么 key 叫 'indian_pines_corrected' 因为 MATLAB 文件里面变量名就是这个。 MATLAB 保存数据时: save('Indian_pines_corrected.mat','indian_pines_corrected') MATLAB变量: indian_pines_corrected Python读取时: dict['indian_pines_corrected'] 四、为什么取出来不是字典而是 array 字典结构是: { key1: value1, key2: value2 } 例如: person = { "name": "Tom", "age": 20 } 取: person["name"] 得到: "Tom" 不是字典。 同理: tmp = { 'indian_pines_corrected': numpy_array } 取: tmp['indian_pines_corrected'] 得到: numpy_array 五、为什么 value 是 array 因为 .mat 文件里存的是矩阵。 MATLAB: 145 × 145 × 200 matrix Python读出来: numpy.ndarray 所以: type(data) 其实是: numpy.ndarray 六、完整执行过程 真实执行过程: tmp = sio.loadmat('../data/Indian_pines_corrected.mat') 得到: tmp = { '__header__': ..., '__version__': ..., '__globals__': [], 'indian_pines_corrected': ndarray(145,145,200) } 然后: data = tmp['indian_pines_corrected'] 得到: numpy.ndarray 七、三种括号总结 符号 含义 () 函数调用 [] 索引 / 取值 {} 字典 例子: dict = {'a':10} dict['a'] # 取值 八、在 NumPy 中 [] 还可以表示 例如: array[0] array[0,1] array[:,2] 这是: 数组索引 九、你这行代码其实做了三件事 data = sio.loadmat('../data/Indian_pines_corrected.mat')['indian_pines_corrected'] 真实逻辑: 读取MAT文件 ↓ 得到字典 ↓ 取字典中变量 ↓ 得到NumPy数组 十、如果你打印看看 你可以试试: tmp = sio.loadmat('../data/Indian_pines_corrected.mat') print(type(tmp)) print(tmp.keys()) print(type(tmp['indian_pines_corrected'])) 你会看到: dict dict_keys([...]) numpy.ndarray 如果你愿意,我可以继续给你讲 这个项目最关键的一段代码: createImageCubes() 这其实是 SSFTT 的核心数据处理步骤,涉及到: patch extraction tokenization Transformer 输入结构 理解它,你就能真正看懂这篇论文的实现。项目的整体结构HSI_SSFTT-main│├── README.md├── data│ ├── Indian_pines_corrected.mat│ └── Indian_pines_gt.mat│└── cls_SSFTT_IP ├── IP_train.py ⭐ 主程序(训练+测试) ├── SSFTTnet.py ⭐Transformer模型定义 ├── get_cls_map.py ⭐生成分类图 ├──classification_maps ├──cls_params └──cls_result代码分析import numpy as np导入 Python 的 NumPy 数值计算库,并把它简称为np在IP_train.py里,np主要干:(1)创建数据数组:patchesData = np.zeros(...)生成一个全零矩阵。(2)reshape 数据:newX = np.reshape(X, (-1, X.shape[2]))改变数组形状。(3)PCA前的数据处理X_pca = applyPCA(X)PCA输入输出都是 NumPy 数组。在深度学习里的关系:数据流程一般是NumPy array ↓ Tensor (PyTorch) ↓ Neural Network在代码里就是:torch.FloatTensor(Xtrain)把 NumPy 转成PyTorch tensor。这段代码整体的逻辑Xtrain = Xtrain.reshape(...) Xtrain = Xtrain.transpose(...) self.x_data = torch.FloatTensor(Xtrain)意思是:numpy——reshape——transpose——转换为torch tensor——输入:Transformer1.几乎所有的科学数据库,都要用到numpy。(1)matlab ,用到matrix;python 用到numpy array(2)需要用到numpy的科学数据库:scipy,scikit learn, PCA, signal processing, FFT, image processing(3)scikit-learn的PCA 输入必须是 NumPy array。2.所以数据流程【为什么需要pca?PCA作用?】.mat 数据 ↓ NumPy array ↓ PCA ↓ patch extraction4.为什么深度学习要tensor?因为GPU计算需要 Tensor,而不是 NumPy array。PyTorch / TensorFlow都使用Tensor(张量)5.Tensor 的特点:5.1可以放到 GPUtensor.to(device)5.2支持自动求导loss.backward()5.3支持深度学习运算图5.4支持 CUDA / MPS / TPU代码分析【】1.def _weights_init(m): classname = m.__class__.__name__ #print(classname) if isinstance(m, nn.Linear) or isinstance(m, nn.Conv3d): init.kaiming_normal_(m.weight)作用:这是一个权重初始化函数。意思是:如果某一层是:nn.Linearnn.Conv3d就给它的权重做kaiming_normal_初始化。【】2.Transformer 的基础零件(这部分不是 SSFTT 特有的,而是标准 transformer encoder 的搭法。)--【】零件1-residualclass Residual(nn.Module): def __init__(self, fn): super().__init__() self.fn = fn def forward(self, x, **kwargs): return self.fn(x, **kwargs) + x作用:残差连接好处/残差连接的作用:(1)保留原始信息(2)减轻深层训练困难(3)让梯度更容易传播【】零件2--LayerNormalizeclass LayerNormalize(nn.Module): def __init__(self, dim, fn): super().__init__() self.norm = nn.LayerNorm(dim) self.fn = fn def forward(self, x, **kwargs): return self.fn(self.norm(x), **kwargs)作用:先 LayerNorm,再送进子模块也就是:为什么叫 PreNorm因为 normalization 放在模块前面,所以这其实就是常说的PreNorm。【】零件3--MLP_Blockclass MLP_Block(nn.Module): def __init__(self, dim, hidden_dim, dropout=0.1): super().__init__() self.net = nn.Sequential( nn.Linear(dim, hidden_dim), nn.GELU(), nn.Dropout(dropout), nn.Linear(hidden_dim, dim), nn.Dropout(dropout) ) def forward(self, x): return self.net(x)作用这是 transformer encoder 里的前馈网络(FeedForward)。标准结构就是:Linear → GELU → Dropout → Linear → Dropout输入输出如果输入是:(b, n, dim),输出还是:(b, n, dim)。因为它中间先升维到hidden_dim,然后又投影回dim。【】零件4:Attention这是 transformer 最核心的部分。(1)初始化部分class Attention(nn.Module): def __init__(self, dim, heads=8, dropout=0.1): super().__init__() self.heads = heads self.scale = dim ** -0.5self.scale = dim ** -0.5这就是:用于缩放点积 attention,防止数值过大。严格来说,论文通常写的是:这里代码用了dim ** -0.5,等效上是在做同样的缩放。(2)to_qkvself.to_qkv = nn.Linear(dim, dim * 3, bias=True)作用一次性把输入映射成:QKV所以输出维度是3 * dim。如果输入 token 是(b, n, 64),那输出就是(b, n, 192),然后再切成 3 份(3)nn1self.nn1 = nn.Linear(dim, dim) self.do1 = nn.Dropout(dropout)作用这是 multi-head attention 输出后,再做一次线性映射。相当于标准 transformer 里的 output projection。(4)forward 开始b, n, _, h = *x.shape, self.heads如果输入x是:(b, n, dim)那么:b= batch sizen= token 数_= dimh= heads比如在这个模型里:dim = 64heads = 8则每个 head 的维度大致是64 / 8 = 8(5)生成 q, k, vqkv = self.to_qkv(x).chunk(3, dim = -1) q, k, v = map(lambda t: rearrange(t, 'b n (h d) - b h n d', h=h), qkv)第一步self.to_qkv(x)先把(b,n,64)变成(b,n,19