diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..4890b292eba662ce83a8ec0cd344504310d446f9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -33,3 +33,33 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text +11522105.pdf filter=lfs diff=lfs merge=lfs -text +results/loss_curves_modelA_20241207_145904.png filter=lfs diff=lfs merge=lfs -text +results/loss_curves_modelB_20241207_151513.png filter=lfs diff=lfs merge=lfs -text +results/loss_curves_modelC_20241207_153102.png filter=lfs diff=lfs merge=lfs -text +results/modelC_scatter_pH.in.CaCl2_20241207_153102.png filter=lfs diff=lfs merge=lfs -text +results/training_metrics_modelA_20241207_145904.png filter=lfs diff=lfs merge=lfs -text +results/training_metrics_modelB_20241207_151513.png filter=lfs diff=lfs merge=lfs -text +results/training_metrics_modelC_20241207_153102.png filter=lfs diff=lfs merge=lfs -text +results1/loss_curves_modelC_20241207_155007_Abs-SG0.png filter=lfs diff=lfs merge=lfs -text +results1/loss_curves_modelC_20241207_160632_Abs-SG0-SNV.png filter=lfs diff=lfs merge=lfs -text +results1/loss_curves_modelC_20241207_162218_Abs-SG1.png filter=lfs diff=lfs merge=lfs -text +results1/loss_curves_modelC_20241207_163811_Abs-SG1-SNV.png filter=lfs diff=lfs merge=lfs -text +results1/loss_curves_modelC_20241207_165344_Abs-SG2.png filter=lfs diff=lfs merge=lfs -text +results1/loss_curves_modelC_20241207_170911_Abs-SG2-SNV.png filter=lfs diff=lfs merge=lfs -text +results1/training_metrics_modelC_20241207_155007_Abs-SG0.png filter=lfs diff=lfs merge=lfs -text +results1/training_metrics_modelC_20241207_160632_Abs-SG0-SNV.png filter=lfs diff=lfs merge=lfs -text +results1/training_metrics_modelC_20241207_162218_Abs-SG1.png filter=lfs diff=lfs merge=lfs -text +results1/training_metrics_modelC_20241207_163811_Abs-SG1-SNV.png filter=lfs diff=lfs merge=lfs -text +results1/training_metrics_modelC_20241207_165344_Abs-SG2.png filter=lfs diff=lfs merge=lfs -text +results1/training_metrics_modelC_20241207_170911_Abs-SG2-SNV.png filter=lfs diff=lfs merge=lfs -text +results2/loss_curves_modelC_20241213_202047_5.png filter=lfs diff=lfs merge=lfs -text +results2/loss_curves_modelC_20241213_203438_10.png filter=lfs diff=lfs merge=lfs -text +results2/loss_curves_modelC_20241213_204103_15.png filter=lfs diff=lfs merge=lfs -text +results2/training_metrics_modelC_20241213_202047_5.png filter=lfs diff=lfs merge=lfs -text +results2/training_metrics_modelC_20241213_203438_10.png filter=lfs diff=lfs merge=lfs -text +results2/training_metrics_modelC_20241213_204103_15.png filter=lfs diff=lfs merge=lfs -text +results3/loss_curves_modelC_20241213_211327_15.png filter=lfs diff=lfs merge=lfs -text +results3/training_metrics_modelC_20241213_211327_15.png filter=lfs diff=lfs merge=lfs -text +shap_summary_plot.png filter=lfs diff=lfs merge=lfs -text +shap_top10_wavelengths.png filter=lfs diff=lfs merge=lfs -text diff --git a/11522105.pdf b/11522105.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a4f00c38331569e73fdccf83e8c8a494f9e4facb --- /dev/null +++ b/11522105.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d350619292928420963d55ea260c286edc0ccad267f2bbefa9b97cee4ee3661 +size 2284006 diff --git a/data_load.py b/data_load.py new file mode 100644 index 0000000000000000000000000000000000000000..d6996f3c59cac6fafb4976c654f29e595afebbeb --- /dev/null +++ b/data_load.py @@ -0,0 +1,48 @@ +import numpy as np +import pandas as pd +from sklearn.model_selection import train_test_split + +def load_soil_data(file_path, target_columns): + """ + 参数: + - file_path: 包含土壤数据的文件路径(假设为CSV格式) + - target_columns: 列表,包含8个目标土壤指标的列名 + + 返回: + - X_train, X_test, y_train, y_test: 训练和测试集的特征和目标值,划分为8:2 + - wavelengths: 波长信息数组 + """ + # 读取CSV文件 + data = pd.read_csv(file_path) + + # 提取波长信息(波长在前4200列的列头中) + wavelengths = data.columns[:4200].str.replace('spc.', '').astype(float) + + # 假设每个Record包含4200个数据点 + X = data.iloc[:, :4200].values # 取前4200列作为特征 + y = data[target_columns].values # 取目标列作为标签 + + # 确保特征数据是浮点数类型 + X = X.astype('float32') + + # 分割数据集为训练集和测试集,训练集占80% + X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) + + # 将特征数据重塑为ResNet模型的输入形状 + # 对于1D卷积,我们需要(batch_size, channels, sequence_length)的形状 + X_train = X_train.reshape(-1, 1, 4200) # 一个通道,序列长度为4200 + X_test = X_test.reshape(-1, 1, 4200) + + return X_train, X_test, y_train, y_test, wavelengths + +if __name__ == "__main__": + # 使用示例 + file_path = 'LUCAS.2009_abs.csv' + target_columns = ['pH.in.CaCl2', 'pH.in.H2O', 'OC', 'CaCO3', 'N', 'P', 'K', 'CEC'] + X_train, X_test, y_train, y_test, wavelengths = load_soil_data(file_path, target_columns) + + print("X_train shape:", X_train.shape) + print("X_test shape:", X_test.shape) + print("y_train shape:", y_train.shape) + print("y_test shape:", y_test.shape) + print("wavelengths shape:", wavelengths.shape) diff --git a/data_processing.py b/data_processing.py new file mode 100644 index 0000000000000000000000000000000000000000..3be22185eef4319ca98390cd89a76c1b51b532e8 --- /dev/null +++ b/data_processing.py @@ -0,0 +1,265 @@ +import numpy as np +from scipy.signal import savgol_filter +import matplotlib.pyplot as plt +from data_load import load_soil_data + +def apply_sg_filter(spectra, window_length=15, polyorder=2, deriv=0): + """ + 应用Savitzky-Golay滤波器进行光谱平滑或求导 + 参数: + - spectra: 输入光谱数据,形状为(n_samples, n_wavelengths) + - window_length: 窗口长度,必须是奇数 + - polyorder: 多项式最高阶数 + - deriv: 求导阶数,0表示平滑,1表示一阶导数,2表示二阶导数 + 返回: + - 处理后的光谱数据 + """ + return np.array([savgol_filter(spectrum, window_length, polyorder, deriv=deriv) + for spectrum in spectra]) + + +def apply_snv(spectra): + """ + 应用标准正态变量(SNV)转换 (标准正态变量变换) + 参数: + - spectra: 输入光谱数据,形状为(n_samples, n_wavelengths) + + 返回: + - SNV处理后的光谱数据 + """ + # 对每个样本进行SNV转换 + spectra_snv = np.zeros_like(spectra) + for i in range(spectra.shape[0]): + spectrum = spectra[i] + # 计算均值和标准差 + mean = np.mean(spectrum) + std = np.std(spectrum) + # 应用SNV转换 + spectra_snv[i] = (spectrum - mean) / std + return spectra_snv + + + + +def process_spectra(spectra, method='Abs-SG0'): + """ + 根据指定方法处理光谱数据 + + 参数: + - spectra: 输入光谱数据,形状为(n_samples, n_wavelengths) + - method: 处理方法,可选值包括: + 'Abs-SG0': SG平滑 + 'Abs-SG0-SNV': SG平滑+SNV + 'Abs-SG1': SG一阶导 + 'Abs-SG1-SNV': SG一阶导+SNV + 'Abs-SG2': SG二阶导 + 'Abs-SG2-SNV': SG二阶导+SNV + + 返回: + - 处理后的光谱数据 + """ + if method == 'Abs-SG0': + return apply_sg_filter(spectra, deriv=0) + elif method == 'Abs-SG0-SNV': + sg_spectra = apply_sg_filter(spectra, deriv=0) + return apply_snv(sg_spectra) + elif method == 'Abs-SG1': + return apply_sg_filter(spectra, deriv=1) + elif method == 'Abs-SG1-SNV': + sg_spectra = apply_sg_filter(spectra, deriv=1) + return apply_snv(sg_spectra) + elif method == 'Abs-SG2': + return apply_sg_filter(spectra, deriv=2) + elif method == 'Abs-SG2-SNV': + sg_spectra = apply_sg_filter(spectra, deriv=2) + return apply_snv(sg_spectra) + else: + raise ValueError(f"Unsupported method: {method}") + + + + +def remove_wavelength_bands(spectra, wavelengths): + """ + 移除400-499.5nm和2450-2499.5nm的波段 + + 参数: + - spectra: 输入光谱数据,形状为(n_samples, n_wavelengths) + - wavelengths: 波长值数组 + + 返回: + - 处理后的光谱数据和对应的波长值 + """ + # 创建掩码,保留所需波段 + mask = ~((wavelengths >= 400) & (wavelengths <= 499.5) | + (wavelengths >= 2450) & (wavelengths <= 2499.5)) + + # 应用掩码 + filtered_spectra = spectra[:, mask] + filtered_wavelengths = wavelengths[mask] + + return filtered_spectra, filtered_wavelengths + + + + +def downsample_spectra(spectra, wavelengths, bin_size): + """ + 对光谱数据进行降采样 + 参数: + - spectra: 输入光谱数据,形状为(n_samples, n_wavelengths) + - wavelengths: 波长值数组 + - bin_size: 降采样窗口大小(5nm、10nm或15nm) + + 返回: + - 降采样后的光谱数据和对应的波长值 + """ + # 计算每个bin的边界 + bins = np.arange(wavelengths[0], wavelengths[-1] + bin_size, bin_size) + + # 初始化结果数组 + n_bins = len(bins) - 1 + downsampled_spectra = np.zeros((spectra.shape[0], n_bins)) + downsampled_wavelengths = np.zeros(n_bins) + + # 对每个bin进行平均 + for i in range(n_bins): + mask = (wavelengths >= bins[i]) & (wavelengths < bins[i+1]) + if np.any(mask): + downsampled_spectra[:, i] = np.mean(spectra[:, mask], axis=1) + downsampled_wavelengths[i] = np.mean([bins[i], bins[i+1]]) + + return downsampled_spectra, downsampled_wavelengths + + + + + +def preprocess_with_downsampling(spectra, wavelengths, bin_size=5): + """ + 完整的预处理流程:移除特定波段并进行降采样 + + 参数: + - spectra: 输入光谱数据,形状为(n_samples, n_wavelengths) + - wavelengths: 波长值数组 + - bin_size: 降采样窗口大小(5nm、10nm或15nm) + + 返回: + - 处理后的光谱数据和对应的波长值 + """ + # 首先移除指定波段 + filtered_spectra, filtered_wavelengths = remove_wavelength_bands(spectra, wavelengths) + + # 然后进行降采样 + downsampled_spectra, downsampled_wavelengths = downsample_spectra( + filtered_spectra, filtered_wavelengths, bin_size) + + return downsampled_spectra, downsampled_wavelengths + + + + +def plot_processed_spectra_with_range(original_spectra, wavelengths=None): + """ + 绘制处理方法的光谱图,包括平均曲线和范围 + + 参数: + - original_spectra: 原始光谱数据,形状为(n_samples, n_wavelengths) + - wavelengths: 波长值,如果为None则使用索引值 + """ + methods = ['Abs-SG0', 'Abs-SG0-SNV', 'Abs-SG1', + 'Abs-SG1-SNV', 'Abs-SG2', 'Abs-SG2-SNV'] + + if wavelengths is None: + wavelengths = np.arange(original_spectra.shape[1]) + + fig, axes = plt.subplots(2, 3, figsize=(18, 10)) # 布局:2行3列 + axes = axes.ravel() + + for i, method in enumerate(methods): + processed = process_spectra(original_spectra, method) # 获取处理后的数据 + mean_curve = np.mean(processed, axis=0) # 平均光谱曲线 + min_curve = np.min(processed, axis=0) # 最小值光谱 + max_curve = np.max(processed, axis=0) # 最大值光谱 + + # 绘制范围 + axes[i].fill_between(wavelengths, min_curve, max_curve, color='skyblue', alpha=0.3, label='Range') + # 绘制平均曲线 + axes[i].plot(wavelengths, mean_curve, color='steelblue', label='Average Curve') + + # 设置标题和图例 + axes[i].set_title(f'({chr(97 + i)}) {method}', loc='center', fontsize=12) # a, b, c... + axes[i].set_xlabel('Wavelength/nm', fontsize=10) + axes[i].set_ylabel('Absorbance', fontsize=10) + axes[i].legend() + axes[i].grid(True) + + # 调整布局 + plt.tight_layout(h_pad=2.5, w_pad=3.0) + plt.show() + + + + + + + +# 示例调用 +if __name__ == '__main__': + # 1. 加载数据 + file_path = 'LUCAS.2009_abs.csv' + target_columns = ['pH.in.CaCl2', 'pH.in.H2O', 'OC', 'CaCO3', 'N', 'P', 'K', 'CEC'] + X_train, X_test, y_train, y_test ,wavelengths= load_soil_data(file_path, target_columns) + + # 2. 将数据重塑为2D + X_train_2d = X_train.reshape(X_train.shape[0], -1) + + # 4. 展示原始数据的光谱处理结果 + print("\n=== 光谱预处理结果 ===") + plot_processed_spectra_with_range(X_train_2d, wavelengths) + + # 5. 移除特定波段并进行不同程度的降采样 + print("\n=== 波段移除和降采样结果 ===") + bin_sizes = [5, 10, 15] # 不同的降采样窗口大小 + + # 为不同的降采样结果创建一个新的图 + plt.figure(figsize=(15, 5)) + + for i, bin_size in enumerate(bin_sizes): + # 处理数据 + processed_spectra, processed_wavelengths = preprocess_with_downsampling( + X_train_2d, wavelengths, bin_size) + + # 打印信息 + print(f"\n使用 {bin_size}nm 降采样:") + print(f"处理后的光谱形状: {processed_spectra.shape}") + print(f"波长数量: {len(processed_wavelengths)}") + + # 绘制降采样结果 + plt.subplot(1, 3, i+1) + mean_curve = np.mean(processed_spectra, axis=0) + std_curve = np.std(processed_spectra, axis=0) + + plt.plot(processed_wavelengths, mean_curve, 'b-', label=f'Mean ({bin_size}nm)') + plt.fill_between(processed_wavelengths, + mean_curve - std_curve, + mean_curve + std_curve, + color='skyblue', alpha=0.2, label='Standard Deviation Range') + plt.title(f'Downsampling {bin_size}nm\n(Wavelengths: {len(processed_wavelengths)})') + plt.xlabel('Wavelength (nm)') + plt.ylabel('Absorbance') + plt.legend() + plt.grid(True) + + plt.tight_layout() + plt.show() + + # 6. 展示完整预处理流程的示例 + print("\n=== 完整预处理流程示例 ===") + # 先进行光谱预处理 + processed_spectra = process_spectra(X_train_2d, method='Abs-SG0-SNV') + # 然后进行波段移除和降采样 + final_spectra, final_wavelengths = preprocess_with_downsampling( + processed_spectra, wavelengths, bin_size=10) + print(f"最终处理后的数据形状: {final_spectra.shape}") + print(f"最终波长数量: {len(final_wavelengths)}") diff --git a/resnet1d.py b/resnet1d.py new file mode 100644 index 0000000000000000000000000000000000000000..caafdbef63d7da18ba04601ceebc0271b93e8607 --- /dev/null +++ b/resnet1d.py @@ -0,0 +1,297 @@ +""" +resnet for 1-d signal data, pytorch version + +Shenda Hong, Oct 2019 +""" + +import numpy as np +from collections import Counter +from tqdm import tqdm +from matplotlib import pyplot as plt +from sklearn.metrics import classification_report + +import torch +import torch.nn as nn +import torch.optim as optim +import torch.nn.functional as F +from torch.utils.data import Dataset, DataLoader + +class MyDataset(Dataset): + def __init__(self, data, label): + self.data = data + self.label = label + + def __getitem__(self, index): + return (torch.tensor(self.data[index], dtype=torch.float), torch.tensor(self.label[index], dtype=torch.long)) + + def __len__(self): + return len(self.data) + +class MyConv1dPadSame(nn.Module): + """ + extend nn.Conv1d to support SAME padding + """ + def __init__(self, in_channels, out_channels, kernel_size, stride, groups=1): + super(MyConv1dPadSame, self).__init__() + self.in_channels = in_channels + self.out_channels = out_channels + self.kernel_size = kernel_size + self.stride = stride + self.groups = groups + self.conv = torch.nn.Conv1d( + in_channels=self.in_channels, + out_channels=self.out_channels, + kernel_size=self.kernel_size, + stride=self.stride, + groups=self.groups) + + def forward(self, x): + + net = x + + # compute pad shape + in_dim = net.shape[-1] + out_dim = (in_dim + self.stride - 1) // self.stride + p = max(0, (out_dim - 1) * self.stride + self.kernel_size - in_dim) + pad_left = p // 2 + pad_right = p - pad_left + net = F.pad(net, (pad_left, pad_right), "constant", 0) + + net = self.conv(net) + + return net + +class MyMaxPool1dPadSame(nn.Module): + """ + extend nn.MaxPool1d to support SAME padding + """ + def __init__(self, kernel_size): + super(MyMaxPool1dPadSame, self).__init__() + self.kernel_size = kernel_size + self.stride = 1 + self.max_pool = torch.nn.MaxPool1d(kernel_size=self.kernel_size) + + def forward(self, x): + + net = x + + # compute pad shape + in_dim = net.shape[-1] + out_dim = (in_dim + self.stride - 1) // self.stride + p = max(0, (out_dim - 1) * self.stride + self.kernel_size - in_dim) + pad_left = p // 2 + pad_right = p - pad_left + net = F.pad(net, (pad_left, pad_right), "constant", 0) + + net = self.max_pool(net) + + return net + +class BasicBlock(nn.Module): + """ + ResNet Basic Block + """ + def __init__(self, in_channels, out_channels, kernel_size, stride, groups, downsample, use_bn, use_do, is_first_block=False): + super(BasicBlock, self).__init__() + + self.in_channels = in_channels + self.kernel_size = kernel_size + self.out_channels = out_channels + self.stride = stride + self.groups = groups + self.downsample = downsample + if self.downsample: + self.stride = stride + else: + self.stride = 1 + self.is_first_block = is_first_block + self.use_bn = use_bn + self.use_do = use_do + + # the first conv + self.bn1 = nn.BatchNorm1d(in_channels) + self.relu1 = nn.ReLU() + self.do1 = nn.Dropout(p=0.5) + self.conv1 = MyConv1dPadSame( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=self.stride, + groups=self.groups) + + # the second conv + self.bn2 = nn.BatchNorm1d(out_channels) + self.relu2 = nn.ReLU() + self.do2 = nn.Dropout(p=0.5) + self.conv2 = MyConv1dPadSame( + in_channels=out_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=1, + groups=self.groups) + + self.max_pool = MyMaxPool1dPadSame(kernel_size=self.stride) + + def forward(self, x): + + identity = x + + # the first conv + out = x + if not self.is_first_block: + if self.use_bn: + out = self.bn1(out) + out = self.relu1(out) + if self.use_do: + out = self.do1(out) + out = self.conv1(out) + + # the second conv + if self.use_bn: + out = self.bn2(out) + out = self.relu2(out) + if self.use_do: + out = self.do2(out) + out = self.conv2(out) + + # if downsample, also downsample identity + if self.downsample: + identity = self.max_pool(identity) + + # if expand channel, also pad zeros to identity + if self.out_channels != self.in_channels: + identity = identity.transpose(-1,-2) + ch1 = (self.out_channels-self.in_channels)//2 + ch2 = self.out_channels-self.in_channels-ch1 + identity = F.pad(identity, (ch1, ch2), "constant", 0) + identity = identity.transpose(-1,-2) + + # shortcut + out += identity + + return out + +class ResNet1D(nn.Module): + """ + + Input: + X: (n_samples, n_channel, n_length) + Y: (n_samples) + + Output: + out: (n_samples) + + Pararmetes: + in_channels: dim of input, the same as n_channel + base_filters: number of filters in the first several Conv layer, it will double at every 4 layers + kernel_size: width of kernel + stride: stride of kernel moving + groups: set larget to 1 as ResNeXt + n_block: number of blocks + n_classes: number of classes + + """ + + def __init__(self, in_channels, base_filters, kernel_size, stride, groups, n_block, n_classes, downsample_gap=2, increasefilter_gap=4, use_bn=True, use_do=True, verbose=False): + super(ResNet1D, self).__init__() + + self.verbose = verbose + self.n_block = n_block + self.kernel_size = kernel_size + self.stride = stride + self.groups = groups + self.use_bn = use_bn + self.use_do = use_do + + self.downsample_gap = downsample_gap # 2 for base model + self.increasefilter_gap = increasefilter_gap # 4 for base model + + # first block + self.first_block_conv = MyConv1dPadSame(in_channels=in_channels, out_channels=base_filters, kernel_size=self.kernel_size, stride=1) + self.first_block_bn = nn.BatchNorm1d(base_filters) + self.first_block_relu = nn.ReLU() + out_channels = base_filters + + # residual blocks + self.basicblock_list = nn.ModuleList() + for i_block in range(self.n_block): + # is_first_block + if i_block == 0: + is_first_block = True + else: + is_first_block = False + # downsample at every self.downsample_gap blocks + if i_block % self.downsample_gap == 1: + downsample = True + else: + downsample = False + # in_channels and out_channels + if is_first_block: + in_channels = base_filters + out_channels = in_channels + else: + # increase filters at every self.increasefilter_gap blocks + in_channels = int(base_filters*2**((i_block-1)//self.increasefilter_gap)) + if (i_block % self.increasefilter_gap == 0) and (i_block != 0): + out_channels = in_channels * 2 + else: + out_channels = in_channels + + tmp_block = BasicBlock( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=self.kernel_size, + stride = self.stride, + groups = self.groups, + downsample=downsample, + use_bn = self.use_bn, + use_do = self.use_do, + is_first_block=is_first_block) + self.basicblock_list.append(tmp_block) + + # final prediction + self.final_bn = nn.BatchNorm1d(out_channels) + self.final_relu = nn.ReLU(inplace=True) + self.do = nn.Dropout(p=0.3) + self.dense = nn.Linear(out_channels, n_classes) + # self.softmax = nn.Softmax(dim=1) + + def forward(self, x): + + out = x + + # first conv + if self.verbose: + print('input shape', out.shape) + out = self.first_block_conv(out) + if self.verbose: + print('after first conv', out.shape) + if self.use_bn: + out = self.first_block_bn(out) + out = self.first_block_relu(out) + + # residual blocks, every block has two conv + for i_block in range(self.n_block): + net = self.basicblock_list[i_block] + if self.verbose: + print('i_block: {0}, in_channels: {1}, out_channels: {2}, downsample: {3}'.format(i_block, net.in_channels, net.out_channels, net.downsample)) + out = net(out) + if self.verbose: + print(out.shape) + + # final prediction + if self.use_bn: + out = self.final_bn(out) + out = self.final_relu(out) + out = out.mean(-1) + if self.verbose: + print('final pooling', out.shape) + # out = self.do(out) + out = self.dense(out) + if self.verbose: + print('dense', out.shape) + # out = self.softmax(out) + if self.verbose: + print('softmax', out.shape) + + return out diff --git a/resnet1d_multitask.py b/resnet1d_multitask.py new file mode 100644 index 0000000000000000000000000000000000000000..64712aad003f7c9feaa695d95a76f59178f69e7d --- /dev/null +++ b/resnet1d_multitask.py @@ -0,0 +1,155 @@ +import torch +import torch.nn as nn +import torchvision.models as models +import resnet1d + +__all__ = ['ResNet1D_MultiTask', 'get_model'] + +class ResNet1D_MultiTask(resnet1d.ResNet1D): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # 获取特征维度 + in_features = self.dense.in_features + + # 移除原始的预测层 + delattr(self, 'dense') + # 添加多任务预测头 + self.prediction_head = nn.Sequential( + # 第一层:512 -> 256 + nn.Linear(in_features, in_features//2), + nn.BatchNorm1d(in_features//2), + nn.ReLU(), + nn.Dropout(p=0.3), + + # 第二层:256 -> 128 + nn.Linear(in_features//2, in_features//4), + nn.BatchNorm1d(in_features//4), + nn.ReLU(), + nn.Dropout(p=0.3), + + # 输出层:128 -> 8 + nn.Linear(in_features//4, 8) + ) + def forward(self, x): + # 获取特征提取器的输出 + out = x + + # first conv + out = self.first_block_conv(out) + if self.use_bn: + out = self.first_block_bn(out) + out = self.first_block_relu(out) + + # residual blocks + for i_block in range(self.n_block): + net = self.basicblock_list[i_block] + out = net(out) + + # 特征聚合 + if self.use_bn: + out = self.final_bn(out) + out = self.final_relu(out) + out = out.mean(-1) # 全局平均池化 + + out=self.prediction_head(out) + + return out # 输出 8 个指标的预测值 + + +def get_model(model_type): + if model_type == 'A': # ResNet18 + return ResNet1D_MultiTask( + in_channels=1, + base_filters=32, # 减小base_filters,降低显存占用 + kernel_size=3, # 使用3x3卷积核 + stride=2, + groups=1, + n_block=8, # ResNet18的配置 + n_classes=8 + ) + elif model_type == 'B': # ResNet34 + return ResNet1D_MultiTask( + in_channels=1, + base_filters=32, # 调整base_filters + kernel_size=3, # 使用3x3卷积核 + stride=2, + groups=1, + n_block=16, # ResNet34的配置 + n_classes=8 + ) + elif model_type == 'C': # ResNet50 + return ResNet1D_MultiTask( + in_channels=1, + base_filters=32, # 调整base_filters + kernel_size=3, # 使用3x3卷积核 + stride=2, + groups=1, + n_block=24, # ResNet50的配置 + n_classes=8 + ) + else: + raise ValueError("Invalid model type. Choose 'A' for ResNet18, 'B' for ResNet34, or 'C' for ResNet50") + +def print_model_info(): + """ + 打印模型关键信息(简化版) + """ + try: + from torchsummary import summary + except ImportError: + print("请先安装torchsummary: pip install torchsummary") + return + + import torch + device = torch.device("cpu") + + model_types = ['A', 'B', 'C'] + model_names = { + 'A': 'ResNet18', + 'B': 'ResNet34', + 'C': 'ResNet50' + } + + # 模型配置信息 + model_configs = { + 'A': {'n_block': 8, 'base_filters': 32, 'kernel_size': 3}, + 'B': {'n_block': 16, 'base_filters': 32, 'kernel_size': 3}, + 'C': {'n_block': 24, 'base_filters': 32, 'kernel_size': 3} + } + + print("\n" + "="*50) + print(f"{'LUCAS土壤光谱分析模型架构':^48}") + print("="*50) + print(f"{'输入: (batch_size=15228, channels=1, length=130)':^48}") + print(f"{'输出: 8个土壤属性预测值':^48}") + print("-"*50) + + for model_type in model_types: + model = get_model(model_type).to(device) + config = model_configs[model_type] + total_params = sum(p.numel() for p in model.parameters()) + trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad) + + print(f"\n[Model {model_type}: {model_names[model_type]}]") + print(f"网络深度: {config['n_block']} blocks") + print(f"基础通道数: {config['base_filters']}") + print(f"卷积核大小: {config['kernel_size']}") + print(f"总参数量: {total_params:,}") + print(f"可训练参数: {trainable_params:,}") + + # 只打印主要层的信息 + main_layers = {} + for name, module in model.named_children(): + params = sum(p.numel() for p in module.parameters()) + if params > 0 and params/total_params > 0.05: # 只显示占比>5%的层 + main_layers[name] = params + + if main_layers: + print("\n主要层结构:") + for name, params in main_layers.items(): + print(f" {name:15}: {params:,} ({params/total_params*100:.1f}%)") + print("-"*50) + +if __name__ == '__main__': + print_model_info() \ No newline at end of file diff --git a/results/loss_curves_modelA_20241207_145904.png b/results/loss_curves_modelA_20241207_145904.png new file mode 100644 index 0000000000000000000000000000000000000000..a6eeb4eb174d4b43d7c9fc615642e5b2aafccf25 --- /dev/null +++ b/results/loss_curves_modelA_20241207_145904.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74563cdb0e349d35f03ccb4052b911d4efb2cd269918d051db9fa89d71f53fac +size 344228 diff --git a/results/loss_curves_modelB_20241207_151513.png b/results/loss_curves_modelB_20241207_151513.png new file mode 100644 index 0000000000000000000000000000000000000000..64e2c2ce201f639ec8edb7a72b9ea5969e1a67ab --- /dev/null +++ b/results/loss_curves_modelB_20241207_151513.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:28f3c1684d86887403a35aeacc30f3a9a0f8d459ab7747501016ad17b53f6dc8 +size 324412 diff --git a/results/loss_curves_modelC_20241207_153102.png b/results/loss_curves_modelC_20241207_153102.png new file mode 100644 index 0000000000000000000000000000000000000000..fa2ea4975952b58155354c9c5776942647cfaf94 --- /dev/null +++ b/results/loss_curves_modelC_20241207_153102.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d295300f658e010fd8e8d1920f5138357df2fcf895b65710b877d659823f75b4 +size 310569 diff --git a/results/metrics_modelA_20241207_145904.txt b/results/metrics_modelA_20241207_145904.txt new file mode 100644 index 0000000000000000000000000000000000000000..1a3778c27babce375d3830d34e60a29ccbfd9ed7 --- /dev/null +++ b/results/metrics_modelA_20241207_145904.txt @@ -0,0 +1,12 @@ +Results for Model A generated at 2024-12-07 14:59:04 +-------------------------------------------------- +Indicator 1 (pH.in.CaCl2) - RMSE: 1.2717, R2: -0.2851 +Indicator 2 (pH.in.H2O) - RMSE: 1.2939, R2: -0.5375 +Indicator 3 (OC) - RMSE: 9.3670, R2: 0.0148 +Indicator 4 (CaCO3) - RMSE: 11.4724, R2: -0.1613 +Indicator 5 (N) - RMSE: 1.8129, R2: 0.1423 +Indicator 6 (P) - RMSE: 6.2296, R2: -0.1063 +Indicator 7 (K) - RMSE: 15.0763, R2: -0.2626 +Indicator 8 (CEC) - RMSE: 3.6354, R2: -0.0210 + +Average Test Loss: 30.3030 diff --git a/results/metrics_modelB_20241207_151513.txt b/results/metrics_modelB_20241207_151513.txt new file mode 100644 index 0000000000000000000000000000000000000000..9a7a3e2e835429cf5665790d898c337ea777c33f --- /dev/null +++ b/results/metrics_modelB_20241207_151513.txt @@ -0,0 +1,12 @@ +Results for Model B generated at 2024-12-07 15:15:13 +-------------------------------------------------- +Indicator 1 (pH.in.CaCl2) - RMSE: 1.2917, R2: -0.3678 +Indicator 2 (pH.in.H2O) - RMSE: 1.2833, R2: -0.4877 +Indicator 3 (OC) - RMSE: 8.2344, R2: 0.4116 +Indicator 4 (CaCO3) - RMSE: 10.9131, R2: 0.0491 +Indicator 5 (N) - RMSE: 1.6586, R2: 0.3991 +Indicator 6 (P) - RMSE: 6.1583, R2: -0.0565 +Indicator 7 (K) - RMSE: 14.5700, R2: -0.1014 +Indicator 8 (CEC) - RMSE: 3.5120, R2: 0.1108 + +Average Test Loss: 27.7304 diff --git a/results/metrics_modelC_20241207_153102.txt b/results/metrics_modelC_20241207_153102.txt new file mode 100644 index 0000000000000000000000000000000000000000..a3dc4a4c4ae729c3d3fbd3844e233c2acb2146a5 --- /dev/null +++ b/results/metrics_modelC_20241207_153102.txt @@ -0,0 +1,12 @@ +Results for Model C generated at 2024-12-07 15:31:02 +-------------------------------------------------- +Indicator 1 (pH.in.CaCl2) - RMSE: 1.1903, R2: 0.0136 +Indicator 2 (pH.in.H2O) - RMSE: 1.1664, R2: -0.0155 +Indicator 3 (OC) - RMSE: 8.0283, R2: 0.4683 +Indicator 4 (CaCO3) - RMSE: 10.7531, R2: 0.1037 +Indicator 5 (N) - RMSE: 1.6682, R2: 0.3850 +Indicator 6 (P) - RMSE: 6.1934, R2: -0.0808 +Indicator 7 (K) - RMSE: 14.2200, R2: 0.0007 +Indicator 8 (CEC) - RMSE: 3.4098, R2: 0.2098 + +Average Test Loss: 26.7518 diff --git a/results/modelC_scatter_CEC_20241207_153102.png b/results/modelC_scatter_CEC_20241207_153102.png new file mode 100644 index 0000000000000000000000000000000000000000..fbec54feca76aed0323ee7e2c3903360ce7e9194 Binary files /dev/null and b/results/modelC_scatter_CEC_20241207_153102.png differ diff --git a/results/modelC_scatter_CaCO3_20241207_153102.png b/results/modelC_scatter_CaCO3_20241207_153102.png new file mode 100644 index 0000000000000000000000000000000000000000..87ae4200eb5433f1a16896f17fb408cf283bc0fc Binary files /dev/null and b/results/modelC_scatter_CaCO3_20241207_153102.png differ diff --git a/results/modelC_scatter_K_20241207_153102.png b/results/modelC_scatter_K_20241207_153102.png new file mode 100644 index 0000000000000000000000000000000000000000..b9c500712d23edb3441b35c81c19cf159b4c8f0a Binary files /dev/null and b/results/modelC_scatter_K_20241207_153102.png differ diff --git a/results/modelC_scatter_N_20241207_153102.png b/results/modelC_scatter_N_20241207_153102.png new file mode 100644 index 0000000000000000000000000000000000000000..325c837ddf93b1f87ac0d2f61392ed35609a3148 Binary files /dev/null and b/results/modelC_scatter_N_20241207_153102.png differ diff --git a/results/modelC_scatter_OC_20241207_153102.png b/results/modelC_scatter_OC_20241207_153102.png new file mode 100644 index 0000000000000000000000000000000000000000..a9770c4e04ea50ba0b5f47211115426b0dcd739d Binary files /dev/null and b/results/modelC_scatter_OC_20241207_153102.png differ diff --git a/results/modelC_scatter_P_20241207_153102.png b/results/modelC_scatter_P_20241207_153102.png new file mode 100644 index 0000000000000000000000000000000000000000..b485a0b585d2a2940ce19e63fcfbd90aacd02336 Binary files /dev/null and b/results/modelC_scatter_P_20241207_153102.png differ diff --git a/results/modelC_scatter_pH.in.CaCl2_20241207_153102.png b/results/modelC_scatter_pH.in.CaCl2_20241207_153102.png new file mode 100644 index 0000000000000000000000000000000000000000..0345a7cf95474efc9482b9c843660f791a3e7333 --- /dev/null +++ b/results/modelC_scatter_pH.in.CaCl2_20241207_153102.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bfe9d7e5c1a9dab36214437abfcb0c5c5c71bd9ba1bd641d8421218fcf1938b0 +size 106742 diff --git a/results/modelC_scatter_pH.in.H2O_20241207_153102.png b/results/modelC_scatter_pH.in.H2O_20241207_153102.png new file mode 100644 index 0000000000000000000000000000000000000000..9a89fb39cd1d27e428ad48b34afe779ef5e39dc4 Binary files /dev/null and b/results/modelC_scatter_pH.in.H2O_20241207_153102.png differ diff --git a/results/training_metrics_modelA_20241207_145904.png b/results/training_metrics_modelA_20241207_145904.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a9519551fb05666af646b7d21bef7bf66036d3 --- /dev/null +++ b/results/training_metrics_modelA_20241207_145904.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbbfd7f34776c3fc177f34f7099fb49a2c2efc9a29ec0cf07232b3adaf8bae9d +size 155289 diff --git a/results/training_metrics_modelB_20241207_151513.png b/results/training_metrics_modelB_20241207_151513.png new file mode 100644 index 0000000000000000000000000000000000000000..a46acbecaad66549927d3fc750eab772b52698c6 --- /dev/null +++ b/results/training_metrics_modelB_20241207_151513.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:38f8c04a46f8f78f3ba13183d7ce323bae2cfde4569558f16b0a93a03433dd42 +size 170721 diff --git a/results/training_metrics_modelC_20241207_153102.png b/results/training_metrics_modelC_20241207_153102.png new file mode 100644 index 0000000000000000000000000000000000000000..36bba3f2531b11a1d02f5913c0dafcad2d6e05df --- /dev/null +++ b/results/training_metrics_modelC_20241207_153102.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd7aa5d9db3411efeffb12872ee9e97bfad03e8d4e5788b4cdab985ca38b8003 +size 175124 diff --git a/results1/loss_curves_modelC_20241207_155007_Abs-SG0.png b/results1/loss_curves_modelC_20241207_155007_Abs-SG0.png new file mode 100644 index 0000000000000000000000000000000000000000..7e9ef18575b7ecba824b8b7d4183e835a9ef4034 --- /dev/null +++ b/results1/loss_curves_modelC_20241207_155007_Abs-SG0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86157560533edc2e4171a848757e32efe854f18ed01d16bbcb631e0dc48bf14a +size 313197 diff --git a/results1/loss_curves_modelC_20241207_160632_Abs-SG0-SNV.png b/results1/loss_curves_modelC_20241207_160632_Abs-SG0-SNV.png new file mode 100644 index 0000000000000000000000000000000000000000..64927baec0a91cfb3a9eaf44c33ca98978c02e93 --- /dev/null +++ b/results1/loss_curves_modelC_20241207_160632_Abs-SG0-SNV.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a8ee4c97aaac8e3bc5fae6828edef8120b06fd3c935b433c705b3eb1f1fd4c8 +size 304450 diff --git a/results1/loss_curves_modelC_20241207_162218_Abs-SG1.png b/results1/loss_curves_modelC_20241207_162218_Abs-SG1.png new file mode 100644 index 0000000000000000000000000000000000000000..12cf73f976caff96c127aaf1d8acc5f04aaebdcb --- /dev/null +++ b/results1/loss_curves_modelC_20241207_162218_Abs-SG1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b77caf0b8ccc50954dba9c42fee9e43effd7eb52153449f0ba59d9c9d913207e +size 278670 diff --git a/results1/loss_curves_modelC_20241207_163811_Abs-SG1-SNV.png b/results1/loss_curves_modelC_20241207_163811_Abs-SG1-SNV.png new file mode 100644 index 0000000000000000000000000000000000000000..570a2836de22d209f6374d53020ac7beb5630f20 --- /dev/null +++ b/results1/loss_curves_modelC_20241207_163811_Abs-SG1-SNV.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15c9a197fa7389591cdf6d770b9f36b2c70d5927708eded6ea601f53ee7d70a1 +size 302123 diff --git a/results1/loss_curves_modelC_20241207_165344_Abs-SG2.png b/results1/loss_curves_modelC_20241207_165344_Abs-SG2.png new file mode 100644 index 0000000000000000000000000000000000000000..90cde00400c252ae1bd6005120b24933746397f2 --- /dev/null +++ b/results1/loss_curves_modelC_20241207_165344_Abs-SG2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:00f3f5b56f2c5439ee63999177b6386b769ac98ae34d4183fa9e58d80323596a +size 303155 diff --git a/results1/loss_curves_modelC_20241207_170911_Abs-SG2-SNV.png b/results1/loss_curves_modelC_20241207_170911_Abs-SG2-SNV.png new file mode 100644 index 0000000000000000000000000000000000000000..5f6dfaf1cc81e8c124813e322a50c9c2dec012bb --- /dev/null +++ b/results1/loss_curves_modelC_20241207_170911_Abs-SG2-SNV.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d46668a66ef6aef1ba63848a207fcae22d0941532146fc9b30dd3dba037f97c6 +size 304392 diff --git a/results1/metrics_modelC_20241210_142058_Abs-SG0.txt b/results1/metrics_modelC_20241210_142058_Abs-SG0.txt new file mode 100644 index 0000000000000000000000000000000000000000..a5068a8282dc6256b824acf490880280f128a07d --- /dev/null +++ b/results1/metrics_modelC_20241210_142058_Abs-SG0.txt @@ -0,0 +1,12 @@ +Results for Model C generated at 2024-12-10 14:20:58 +-------------------------------------------------- +Indicator 1 (pH.in.CaCl2) - RMSE: 1.1329, R2: 0.1905 +Indicator 2 (pH.in.H2O) - RMSE: 1.1135, R2: 0.1565 +Indicator 3 (OC) - RMSE: 8.1022, R2: 0.4485 +Indicator 4 (CaCO3) - RMSE: 10.7757, R2: 0.0961 +Indicator 5 (N) - RMSE: 1.6102, R2: 0.4662 +Indicator 6 (P) - RMSE: 6.1459, R2: -0.0480 +Indicator 7 (K) - RMSE: 13.9761, R2: 0.0675 +Indicator 8 (CEC) - RMSE: 3.2445, R2: 0.3522 + +Average Test Loss: 27.7911 diff --git a/results1/metrics_modelC_20241210_143517_Abs-SG0-SNV.txt b/results1/metrics_modelC_20241210_143517_Abs-SG0-SNV.txt new file mode 100644 index 0000000000000000000000000000000000000000..5f37abe7fac2f99b07ef87ad9b90c00dbffdfec9 --- /dev/null +++ b/results1/metrics_modelC_20241210_143517_Abs-SG0-SNV.txt @@ -0,0 +1,12 @@ +Results for Model C generated at 2024-12-10 14:35:17 +-------------------------------------------------- +Indicator 1 (pH.in.CaCl2) - RMSE: 1.0763, R2: 0.3406 +Indicator 2 (pH.in.H2O) - RMSE: 1.0515, R2: 0.3294 +Indicator 3 (OC) - RMSE: 6.0624, R2: 0.8271 +Indicator 4 (CaCO3) - RMSE: 8.5423, R2: 0.6430 +Indicator 5 (N) - RMSE: 1.4266, R2: 0.6711 +Indicator 6 (P) - RMSE: 6.1328, R2: -0.0392 +Indicator 7 (K) - RMSE: 13.8842, R2: 0.0918 +Indicator 8 (CEC) - RMSE: 3.3248, R2: 0.2857 + +Average Test Loss: 21.7375 diff --git a/results1/metrics_modelC_20241210_144926_Abs-SG1.txt b/results1/metrics_modelC_20241210_144926_Abs-SG1.txt new file mode 100644 index 0000000000000000000000000000000000000000..89f66f8140f7a2d1b3db16351895d3a449a906b4 --- /dev/null +++ b/results1/metrics_modelC_20241210_144926_Abs-SG1.txt @@ -0,0 +1,12 @@ +Results for Model C generated at 2024-12-10 14:49:26 +-------------------------------------------------- +Indicator 1 (pH.in.CaCl2) - RMSE: 1.1943, R2: 0.0002 +Indicator 2 (pH.in.H2O) - RMSE: 1.1764, R2: -0.0507 +Indicator 3 (OC) - RMSE: 9.0927, R2: 0.1252 +Indicator 4 (CaCO3) - RMSE: 11.3249, R2: -0.1028 +Indicator 5 (N) - RMSE: 1.8572, R2: 0.0553 +Indicator 6 (P) - RMSE: 6.0900, R2: -0.0105 +Indicator 7 (K) - RMSE: 14.0982, R2: 0.0345 +Indicator 8 (CEC) - RMSE: 3.6072, R2: 0.0103 + +Average Test Loss: 30.6785 diff --git a/results1/metrics_modelC_20241210_150332_Abs-SG1-SNV.txt b/results1/metrics_modelC_20241210_150332_Abs-SG1-SNV.txt new file mode 100644 index 0000000000000000000000000000000000000000..683c13f55647d5de81232920e720c9ead7c1e2a8 --- /dev/null +++ b/results1/metrics_modelC_20241210_150332_Abs-SG1-SNV.txt @@ -0,0 +1,12 @@ +Results for Model C generated at 2024-12-10 15:03:32 +-------------------------------------------------- +Indicator 1 (pH.in.CaCl2) - RMSE: 0.9366, R2: 0.6220 +Indicator 2 (pH.in.H2O) - RMSE: 0.9237, R2: 0.6007 +Indicator 3 (OC) - RMSE: 6.9911, R2: 0.6943 +Indicator 4 (CaCO3) - RMSE: 7.3689, R2: 0.8023 +Indicator 5 (N) - RMSE: 1.6105, R2: 0.4658 +Indicator 6 (P) - RMSE: 5.9795, R2: 0.0609 +Indicator 7 (K) - RMSE: 13.8154, R2: 0.1097 +Indicator 8 (CEC) - RMSE: 3.5810, R2: 0.0387 + +Average Test Loss: 21.2949 diff --git a/results1/metrics_modelC_20241210_151845_Abs-SG2.txt b/results1/metrics_modelC_20241210_151845_Abs-SG2.txt new file mode 100644 index 0000000000000000000000000000000000000000..1c64be0f7c5a999dbbbe714203ff820ca42b0664 --- /dev/null +++ b/results1/metrics_modelC_20241210_151845_Abs-SG2.txt @@ -0,0 +1,12 @@ +Results for Model C generated at 2024-12-10 15:18:45 +-------------------------------------------------- +Indicator 1 (pH.in.CaCl2) - RMSE: 1.5963, R2: -2.1907 +Indicator 2 (pH.in.H2O) - RMSE: 1.5703, R2: -2.3361 +Indicator 3 (OC) - RMSE: 9.5270, R2: -0.0543 +Indicator 4 (CaCO3) - RMSE: 11.4900, R2: -0.1685 +Indicator 5 (N) - RMSE: 1.9782, R2: -0.2159 +Indicator 6 (P) - RMSE: 6.6043, R2: -0.3975 +Indicator 7 (K) - RMSE: 15.9916, R2: -0.5983 +Indicator 8 (CEC) - RMSE: 4.1587, R2: -0.7483 + +Average Test Loss: 35.7320 diff --git a/results1/metrics_modelC_20241210_153333_Abs-SG2-SNV.txt b/results1/metrics_modelC_20241210_153333_Abs-SG2-SNV.txt new file mode 100644 index 0000000000000000000000000000000000000000..f9ca8eaf0221fc91fd0f3b86b8d553f2e0a99532 --- /dev/null +++ b/results1/metrics_modelC_20241210_153333_Abs-SG2-SNV.txt @@ -0,0 +1,12 @@ +Results for Model C generated at 2024-12-10 15:33:33 +-------------------------------------------------- +Indicator 1 (pH.in.CaCl2) - RMSE: 0.8667, R2: 0.7227 +Indicator 2 (pH.in.H2O) - RMSE: 0.8609, R2: 0.6986 +Indicator 3 (OC) - RMSE: 5.8075, R2: 0.8544 +Indicator 4 (CaCO3) - RMSE: 6.3981, R2: 0.8877 +Indicator 5 (N) - RMSE: 1.4128, R2: 0.6836 +Indicator 6 (P) - RMSE: 5.8376, R2: 0.1469 +Indicator 7 (K) - RMSE: 12.9567, R2: 0.3112 +Indicator 8 (CEC) - RMSE: 3.2131, R2: 0.3770 + +Average Test Loss: 17.8127 diff --git a/results1/training_metrics_modelC_20241207_155007_Abs-SG0.png b/results1/training_metrics_modelC_20241207_155007_Abs-SG0.png new file mode 100644 index 0000000000000000000000000000000000000000..9f3cc71c043e23d063ec443edf026710f6a623e9 --- /dev/null +++ b/results1/training_metrics_modelC_20241207_155007_Abs-SG0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5eb9c756d60256f1a9a46aa56007185b542fca2430ead84ab823d1ad3f42962 +size 182694 diff --git a/results1/training_metrics_modelC_20241207_160632_Abs-SG0-SNV.png b/results1/training_metrics_modelC_20241207_160632_Abs-SG0-SNV.png new file mode 100644 index 0000000000000000000000000000000000000000..e8faf5ab8a08f5b00dee6fac97764f38cf6febca --- /dev/null +++ b/results1/training_metrics_modelC_20241207_160632_Abs-SG0-SNV.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:db3a8dcd6f35edbc30ecc11927b790236a8079662c70af45630f7d0cf66c3388 +size 199867 diff --git a/results1/training_metrics_modelC_20241207_162218_Abs-SG1.png b/results1/training_metrics_modelC_20241207_162218_Abs-SG1.png new file mode 100644 index 0000000000000000000000000000000000000000..8ed6707dcda4b447886ce31471fd93f378465a19 --- /dev/null +++ b/results1/training_metrics_modelC_20241207_162218_Abs-SG1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6171188c2b3ca41de3092d9a900af93679dc7717000a3a829c90de6fc02bf08 +size 211203 diff --git a/results1/training_metrics_modelC_20241207_163811_Abs-SG1-SNV.png b/results1/training_metrics_modelC_20241207_163811_Abs-SG1-SNV.png new file mode 100644 index 0000000000000000000000000000000000000000..b5faeace4f1dfe3dd46860a250a6eb988c8ad3c0 --- /dev/null +++ b/results1/training_metrics_modelC_20241207_163811_Abs-SG1-SNV.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11b719ecb55fc472e68cc4b4f4a6bcf5d0bf824026cb5b07ee26fc2891f7ee83 +size 181692 diff --git a/results1/training_metrics_modelC_20241207_165344_Abs-SG2.png b/results1/training_metrics_modelC_20241207_165344_Abs-SG2.png new file mode 100644 index 0000000000000000000000000000000000000000..745bbbc132a6e7b570f1e94e2848d73e0c181bcd --- /dev/null +++ b/results1/training_metrics_modelC_20241207_165344_Abs-SG2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4417b4e741f765fa7069f9244d7edb369041fefc99cd77a1921fa03fef36ef0e +size 233731 diff --git a/results1/training_metrics_modelC_20241207_170911_Abs-SG2-SNV.png b/results1/training_metrics_modelC_20241207_170911_Abs-SG2-SNV.png new file mode 100644 index 0000000000000000000000000000000000000000..b66e61291e019c988e0f1ee490cae582118b78cd --- /dev/null +++ b/results1/training_metrics_modelC_20241207_170911_Abs-SG2-SNV.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e2c003ada19e55c21ed6f7c8088f0542abde2156ed710beda44f64d63d339bb +size 159577 diff --git a/results2/loss_curves_modelC_20241213_202047_5.png b/results2/loss_curves_modelC_20241213_202047_5.png new file mode 100644 index 0000000000000000000000000000000000000000..df65d21e1f684c60f78a5b7e9f5a7b0f01969cd8 --- /dev/null +++ b/results2/loss_curves_modelC_20241213_202047_5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c42b5de92a5251bef3b112afc3d41fdca416f07b857cb6c97fdd6fadc6bbddba +size 301785 diff --git a/results2/loss_curves_modelC_20241213_203438_10.png b/results2/loss_curves_modelC_20241213_203438_10.png new file mode 100644 index 0000000000000000000000000000000000000000..397d4664fbbe1b3dd378701f1aef9d10fdfe959d --- /dev/null +++ b/results2/loss_curves_modelC_20241213_203438_10.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50d61121f1dd9dc7d1f675ebebc22f8c171a78e663295a2b83a34fdc764196b2 +size 302630 diff --git a/results2/loss_curves_modelC_20241213_204103_15.png b/results2/loss_curves_modelC_20241213_204103_15.png new file mode 100644 index 0000000000000000000000000000000000000000..b52ce68c93e316207526d87d47235f455dbe3bff --- /dev/null +++ b/results2/loss_curves_modelC_20241213_204103_15.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c3a20715b45defca5c73c2ac86cbe403a01f71c38d246a5713070b7d5556c93 +size 301035 diff --git a/results2/metrics_modelC_20241213_202047_5.txt b/results2/metrics_modelC_20241213_202047_5.txt new file mode 100644 index 0000000000000000000000000000000000000000..f9986b77f405101d6c72aad002d9a207a61f9eb3 --- /dev/null +++ b/results2/metrics_modelC_20241213_202047_5.txt @@ -0,0 +1,4 @@ +Results for Model C generated at 2024-12-13 20:20:47 +-------------------------------------------------- + +Average Test Loss: 17.4861 diff --git a/results2/metrics_modelC_20241213_203438_10.txt b/results2/metrics_modelC_20241213_203438_10.txt new file mode 100644 index 0000000000000000000000000000000000000000..0f437edff7f9ed57833672ccb84a4c2fe5e73eec --- /dev/null +++ b/results2/metrics_modelC_20241213_203438_10.txt @@ -0,0 +1,4 @@ +Results for Model C generated at 2024-12-13 20:34:38 +-------------------------------------------------- + +Average Test Loss: 17.7109 diff --git a/results2/metrics_modelC_20241213_204103_15.txt b/results2/metrics_modelC_20241213_204103_15.txt new file mode 100644 index 0000000000000000000000000000000000000000..852e042cd93e14dd0a7a807e5b2ab82b400a1699 --- /dev/null +++ b/results2/metrics_modelC_20241213_204103_15.txt @@ -0,0 +1,4 @@ +Results for Model C generated at 2024-12-13 20:41:03 +-------------------------------------------------- + +Average Test Loss: 17.6297 diff --git a/results2/training_metrics_modelC_20241213_202047_5.png b/results2/training_metrics_modelC_20241213_202047_5.png new file mode 100644 index 0000000000000000000000000000000000000000..17157c2f2e6cdc7ea7fc6b08c5ac8f691dc329d3 --- /dev/null +++ b/results2/training_metrics_modelC_20241213_202047_5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6f15e48e33823fd51940f00e5f9bfe43ee244e0ff4c907a91a0fe220dfb63640 +size 158985 diff --git a/results2/training_metrics_modelC_20241213_203438_10.png b/results2/training_metrics_modelC_20241213_203438_10.png new file mode 100644 index 0000000000000000000000000000000000000000..c31f9045a6d63da005d05ad57d4034c87a8920f4 --- /dev/null +++ b/results2/training_metrics_modelC_20241213_203438_10.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ddb458eb90d8230fce38e3f6112aaf61ef3abc2779ebeb5a41391102f3dcea27 +size 158808 diff --git a/results2/training_metrics_modelC_20241213_204103_15.png b/results2/training_metrics_modelC_20241213_204103_15.png new file mode 100644 index 0000000000000000000000000000000000000000..8a0175bd7f9c92a26c4a57c8543a85aacf869710 --- /dev/null +++ b/results2/training_metrics_modelC_20241213_204103_15.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:31bcfe6c8eb6263411c24c2e36cabf380286d52efb0ecceec634fd58a3d11c1d +size 160661 diff --git a/results3/loss_curves_modelC_20241213_211327_15.png b/results3/loss_curves_modelC_20241213_211327_15.png new file mode 100644 index 0000000000000000000000000000000000000000..f63e9830b7639ac1979541c8ccb8d8495c4d03e0 --- /dev/null +++ b/results3/loss_curves_modelC_20241213_211327_15.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:84a487bbdef9d570e69687e1ae9602c35cb34b05f045bcae9f08b8f8c4b499f7 +size 322033 diff --git a/results3/metrics_modelC_20241213_211327_15.txt b/results3/metrics_modelC_20241213_211327_15.txt new file mode 100644 index 0000000000000000000000000000000000000000..01952c46235537e4b1178b8b4817dfbfb1d8c28d --- /dev/null +++ b/results3/metrics_modelC_20241213_211327_15.txt @@ -0,0 +1,12 @@ +Results for Model C generated at 2024-12-13 21:13:27 +-------------------------------------------------- +Indicator 1 (pH.in.CaCl2) - RMSE: 0.9515, R2: 0.5972 +Indicator 2 (pH.in.H2O) - RMSE: 0.9461, R2: 0.5605 +Indicator 3 (OC) - RMSE: 5.4777, R2: 0.8848 +Indicator 4 (CaCO3) - RMSE: 6.7208, R2: 0.8632 +Indicator 5 (N) - RMSE: 1.4176, R2: 0.6794 +Indicator 6 (P) - RMSE: 6.0383, R2: 0.0234 +Indicator 7 (K) - RMSE: 13.3519, R2: 0.2233 +Indicator 8 (CEC) - RMSE: 3.2411, R2: 0.3550 + +Average Test Loss: 18.6959 diff --git a/results3/modelC_scatter_CEC_20241213_211327.png b/results3/modelC_scatter_CEC_20241213_211327.png new file mode 100644 index 0000000000000000000000000000000000000000..86786d4ae838cdf783fd49180f885eeb8f1744b5 Binary files /dev/null and b/results3/modelC_scatter_CEC_20241213_211327.png differ diff --git a/results3/modelC_scatter_CaCO3_20241213_211327.png b/results3/modelC_scatter_CaCO3_20241213_211327.png new file mode 100644 index 0000000000000000000000000000000000000000..2c34d24e85668d0527deae78a7772b0b8b48a156 Binary files /dev/null and b/results3/modelC_scatter_CaCO3_20241213_211327.png differ diff --git a/results3/modelC_scatter_K_20241213_211327.png b/results3/modelC_scatter_K_20241213_211327.png new file mode 100644 index 0000000000000000000000000000000000000000..fd4863e43ec8d7c4b3f00a6790508e92a6180592 Binary files /dev/null and b/results3/modelC_scatter_K_20241213_211327.png differ diff --git a/results3/modelC_scatter_N_20241213_211327.png b/results3/modelC_scatter_N_20241213_211327.png new file mode 100644 index 0000000000000000000000000000000000000000..cc5fb86db21ec74a6c3797d49770de0bb2682239 Binary files /dev/null and b/results3/modelC_scatter_N_20241213_211327.png differ diff --git a/results3/modelC_scatter_OC_20241213_211327.png b/results3/modelC_scatter_OC_20241213_211327.png new file mode 100644 index 0000000000000000000000000000000000000000..c5c0edd18ba58dab233d4772788173dfe430450d Binary files /dev/null and b/results3/modelC_scatter_OC_20241213_211327.png differ diff --git a/results3/modelC_scatter_P_20241213_211327.png b/results3/modelC_scatter_P_20241213_211327.png new file mode 100644 index 0000000000000000000000000000000000000000..a97399e216f38352c5c149af4f8b817f5943607f Binary files /dev/null and b/results3/modelC_scatter_P_20241213_211327.png differ diff --git a/results3/modelC_scatter_pH.in.CaCl2_20241213_211327.png b/results3/modelC_scatter_pH.in.CaCl2_20241213_211327.png new file mode 100644 index 0000000000000000000000000000000000000000..0091c6822cfe47a8800da20ce2ca18aaa3a8bbee Binary files /dev/null and b/results3/modelC_scatter_pH.in.CaCl2_20241213_211327.png differ diff --git a/results3/modelC_scatter_pH.in.H2O_20241213_211327.png b/results3/modelC_scatter_pH.in.H2O_20241213_211327.png new file mode 100644 index 0000000000000000000000000000000000000000..c40467258407a38e6cbe57dfad7c29426a756091 Binary files /dev/null and b/results3/modelC_scatter_pH.in.H2O_20241213_211327.png differ diff --git a/results3/training_metrics_modelC_20241213_211327_15.png b/results3/training_metrics_modelC_20241213_211327_15.png new file mode 100644 index 0000000000000000000000000000000000000000..babb2225cedd7025ff943e4999174b575115be86 --- /dev/null +++ b/results3/training_metrics_modelC_20241213_211327_15.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ead1ed306fe1f22436483544b9d699384da46d94bcce8d52d9ec633722c25ad2 +size 157972 diff --git a/shap_analysis.py b/shap_analysis.py new file mode 100644 index 0000000000000000000000000000000000000000..c382b6c91898f0370799dfc04edc58d85f36f38f --- /dev/null +++ b/shap_analysis.py @@ -0,0 +1,130 @@ +import torch +import numpy as np +import shap +import matplotlib.pyplot as plt +import seaborn as sns +from data_load import load_soil_data +from data_processing import preprocess_with_downsampling, process_spectra +from resnet1d_multitask import get_model + +# 设置中文字体 +plt.rcParams['font.sans-serif'] = ['SimHei'] +plt.rcParams['axes.unicode_minus'] = False + +def load_model_and_data(): + # 定义目标列 + target_columns = ['pH.in.CaCl2', 'pH.in.H2O', 'OC', 'CaCO3', 'N', 'P', 'K', 'CEC'] + + # 加载数据 + X_train, X_test, y_train, y_test, wavelengths = load_soil_data('LUCAS.2009_abs.csv', target_columns) + + # 确保数据形状为 (n_samples, n_wavelengths) + X_train, X_test = X_train.squeeze(), X_test.squeeze() + + # 预处理数据 + X_train = process_spectra(X_train, 'Abs-SG1-SNV') + X_test = process_spectra(X_test, 'Abs-SG1-SNV') + + X_train, X_train_nwavelengths = preprocess_with_downsampling(X_train, wavelengths, 15) + X_test, X_test_nwavelengths = preprocess_with_downsampling(X_test, wavelengths, 15) + + # 将数据形状调整为 (n_samples, 1, n_wavelengths) + X_train = X_train.reshape(X_train.shape[0], 1, X_train.shape[1]) + X_test = X_test.reshape(X_test.shape[0], 1, X_test.shape[1]) + + # 加载模型 + device = torch.device('cpu') + model = get_model('C') + model.load_state_dict(torch.load('code/models/set.pth', map_location=device)) + model.eval() + + return model, X_test, X_test_nwavelengths + +def explain_predictions(model, X_test, wavelengths, n_background=50, n_samples=100): + """ + 使用KernelExplainer计算SHAP值,更适合CPU环境 + """ + # 定义一个包装函数来处理模型预测 + def model_predict(x): + with torch.no_grad(): + x = torch.FloatTensor(x) + if len(x.shape) == 2: + x = x.reshape(x.shape[0], 1, -1) + output = model(x) + # 如果模型输出是元组,取第一个元素 + if isinstance(output, tuple): + output = output[0] + return output.numpy() + + # 准备背景数据和测试数据 + background_data = X_test[:n_background].squeeze() + test_data = X_test[:n_samples].squeeze() + + # 创建KernelExplainer + print("创建SHAP解释器...") + explainer = shap.KernelExplainer(model_predict, background_data) + + # 计算SHAP值 + print("计算SHAP值(这可能需要几分钟时间)...") + shap_values = explainer.shap_values(test_data, nsamples=100) + + # 处理多输出模型的SHAP值 + if isinstance(shap_values, list): + # 取所有输出的平均值 + avg_shap_values = np.mean([np.abs(sv) for sv in shap_values], axis=0) + else: + avg_shap_values = np.abs(shap_values) + + return avg_shap_values + +def plot_top10_wavelengths(shap_values, wavelengths): + """ + 绘制贡献度排名前10的波段柱状图 + """ + # 计算每个波段的平均绝对SHAP值 + mean_shap = np.mean(np.abs(shap_values), axis=0) + + # 获取前10个波段的索引 + top10_idx = np.argsort(mean_shap)[-10:][::-1] + top10_wavelengths = wavelengths[top10_idx] + top10_values = mean_shap[top10_idx] + + # 创建柱状图 + plt.figure(figsize=(8, 6)) + plt.barh(range(10), top10_values, color='skyblue') + plt.yticks(range(10), [f'{w:.1f}' for w in top10_wavelengths]) + plt.xlabel('mean(|SHAP value|) (average impact on model output magnitude)') + plt.title('Top 10 Wavelengths by SHAP Value') + plt.gca().invert_yaxis() + plt.tight_layout() + plt.savefig('shap_top10_wavelengths.png', dpi=300, bbox_inches='tight') + plt.close() + + +def plot_shap_summary(shap_values, wavelengths): + """ + 绘制SHAP值的蜂窝图 + """ + plt.figure(figsize=(10, 8)) + shap.summary_plot(shap_values, features=wavelengths, feature_names=[f'{w:.1f}' for w in wavelengths], plot_type='dot', show=False) + plt.tight_layout() + plt.savefig('shap_summary_plot.png', dpi=300, bbox_inches='tight') + plt.close() + +def main(): + # 加载模型和数据 + print("加载模型和数据...") + model, X_test, wavelengths = load_model_and_data() + + # 计算SHAP值 + print("正在计算SHAP值...") + shap_values = explain_predictions(model, X_test, wavelengths) + + # 绘制图表 + print("正在生成图表...") + plot_shap_summary(shap_values, wavelengths) + plot_top10_wavelengths(shap_values, wavelengths) + print("分析完成!图表已保存为 'shap_summary_plot.png' 和 'shap_top10_wavelengths.png'") + +if __name__ == "__main__": + main() diff --git a/shap_summary_plot.png b/shap_summary_plot.png new file mode 100644 index 0000000000000000000000000000000000000000..15de40aa19bebb89983cab0af4d2bfbad371f484 --- /dev/null +++ b/shap_summary_plot.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13000bb4d519db2ea75b916e7a3b8e7a8c1a2c7b12c0689d710e336f53e01b22 +size 234471 diff --git a/shap_top10_wavelengths.png b/shap_top10_wavelengths.png new file mode 100644 index 0000000000000000000000000000000000000000..712c9aa272bf9b4b05c50bc7bb66a96f6620efe6 --- /dev/null +++ b/shap_top10_wavelengths.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6788c9423e2fd88b6ec746e19701b6ed731a8b8b0572549efafb3210afa730a9 +size 156823 diff --git a/test.ipynb b/test.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..af352c7dc80b8dd33693acb3a7fcb19c7eebdb0b --- /dev/null +++ b/test.ipynb @@ -0,0 +1,436 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "initial_id", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-03T08:06:19.370069Z", + "start_time": "2024-12-03T08:06:16.509950Z" + }, + "collapsed": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2.5.1+cu118\n", + "True\n", + "CUDA is available. Using NVIDIA GeForce RTX 4060 Ti\n" + ] + } + ], + "source": [ + "import torch.nn as nn\n", + "import torch\n", + "from matplotlib import pyplot as plt\n", + "from torch.utils.data import DataLoader\n", + "from data_load import load_soil_data\n", + "from data_processing import process_spectra\n", + "from data_processing import preprocess_with_downsampling\n", + "from resnet1d_multitask import ResNet1D_MultiTask,get_model" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "58d3d91289cce8d0", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-03T08:07:02.456574Z", + "start_time": "2024-12-03T08:06:51.159946Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X_train shape: (15228, 1, 130)\n", + "y_train shape: (15228, 8)\n", + "X_test shape: (3807, 1, 130)\n", + "y_test shape: (3807, 8)\n" + ] + } + ], + "source": [ + "# 定义目标列\n", + "target_columns = ['pH.in.CaCl2', 'pH.in.H2O', 'OC', 'CaCO3', 'N', 'P', 'K', 'CEC']\n", + "\n", + "# 加载数据\n", + "X_train, X_test, y_train, y_test, wavelengths = load_soil_data('../LUCAS.2009_abs.csv', target_columns)\n", + "\n", + "# 确保数据形状为 (n_samples, n_wavelengths)\n", + "X_train, X_test = X_train.squeeze(), X_test.squeeze()\n", + "\n", + "# 预处理数据\n", + "methods = ['Abs-SG0', 'Abs-SG0-SNV', 'Abs-SG1', \n", + " 'Abs-SG1-SNV', 'Abs-SG2', 'Abs-SG2-SNV']\n", + "bin_sizes = [5,10,15,20] # 不同的降采样窗口大小\n", + "X_train= process_spectra(X_train,methods[3])\n", + "X_test = process_spectra(X_test,methods[3])\n", + "\n", + "X_train,X_train_nwavelengths=preprocess_with_downsampling(X_train,wavelengths,bin_sizes[2])\n", + "X_test,X_test_nwavelengths=preprocess_with_downsampling(X_test,wavelengths,bin_sizes[2])\n", + "# 将数据形状调整为 (n_samples, 1, n_wavelengths)\n", + "X_train = X_train.reshape(X_train.shape[0], 1, X_train.shape[1])\n", + "X_test = X_test.reshape(X_test.shape[0], 1, X_test.shape[1])\n", + "\n", + "# 检查数据形状\n", + "print(\"X_train shape:\", X_train.shape)\n", + "print(\"y_train shape:\", y_train.shape)\n", + "print(\"X_test shape:\", X_test.shape)\n", + "print(\"y_test shape:\", y_test.shape)\n", + "assert X_train.shape[0] == y_train.shape[0], \"Mismatch in number of samples between X_train and y_train\"\n", + "assert X_test.shape[0] == y_test.shape[0], \"Mismatch in number of samples between X_test and y_test\"\n", + "\n", + "# 创建数据加载器\n", + "train_dataset = torch.utils.data.TensorDataset(torch.tensor(X_train, dtype=torch.float32), torch.tensor(y_train, dtype=torch.float32))\n", + "test_dataset = torch.utils.data.TensorDataset(torch.tensor(X_test, dtype=torch.float32), torch.tensor(y_test, dtype=torch.float32))\n", + "\n", + "train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True)\n", + "test_loader = DataLoader(test_dataset, batch_size=256, shuffle=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "9f27bb84eb7012f6", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-03T08:07:08.044106Z", + "start_time": "2024-12-03T08:07:06.486001Z" + } + }, + "outputs": [], + "source": [ + "# 模型参数设置\n", + "model = get_model('C')\n", + "\n", + "# 损失函数\n", + "criterion = nn.SmoothL1Loss()\n", + "\n", + "# 优化器\n", + "optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-5)\n", + "scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.81)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "82865ac358cf6bd", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-03T08:42:05.552224Z", + "start_time": "2024-12-03T08:07:09.843165Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch [1/100], Loss: 33.9448, RMSE: 69.7849, R2: -0.4280\n", + "Epoch [2/100], Loss: 28.8905, RMSE: 63.9639, R2: -0.0229\n", + "Epoch [3/100], Loss: 28.3974, RMSE: 63.0714, R2: 0.0427\n", + "Epoch [4/100], Loss: 28.0716, RMSE: 62.5553, R2: 0.0710\n", + "Epoch [5/100], Loss: 27.4575, RMSE: 61.1024, R2: 0.1529\n", + "Epoch [6/100], Loss: 26.3365, RMSE: 57.5674, R2: 0.2648\n", + "Epoch [7/100], Loss: 25.7161, RMSE: 55.9864, R2: 0.3006\n", + "Epoch [8/100], Loss: 25.4030, RMSE: 55.4972, R2: 0.3164\n", + "Epoch [9/100], Loss: 24.9097, RMSE: 54.3405, R2: 0.3484\n", + "Epoch [10/100], Loss: 23.4980, RMSE: 50.7502, R2: 0.3968\n", + "Epoch [11/100], Loss: 22.9915, RMSE: 49.8383, R2: 0.4053\n", + "Epoch [12/100], Loss: 22.6047, RMSE: 48.6838, R2: 0.4341\n", + "Epoch [13/100], Loss: 22.0536, RMSE: 47.7625, R2: 0.4444\n", + "Epoch [14/100], Loss: 21.8895, RMSE: 47.2756, R2: 0.4584\n", + "Epoch [15/100], Loss: 21.6260, RMSE: 46.7802, R2: 0.4626\n", + "Epoch [16/100], Loss: 21.3705, RMSE: 46.4082, R2: 0.4749\n", + "Epoch [17/100], Loss: 21.2690, RMSE: 46.0298, R2: 0.4839\n", + "Epoch [18/100], Loss: 21.0098, RMSE: 45.7252, R2: 0.4899\n", + "Epoch [19/100], Loss: 21.1423, RMSE: 45.7182, R2: 0.4881\n", + "Epoch [20/100], Loss: 20.7866, RMSE: 45.0235, R2: 0.4993\n", + "Epoch [21/100], Loss: 20.8006, RMSE: 45.1175, R2: 0.5024\n", + "Epoch [22/100], Loss: 20.5781, RMSE: 44.7872, R2: 0.5097\n", + "Epoch [23/100], Loss: 20.5299, RMSE: 44.6490, R2: 0.5120\n", + "Epoch [24/100], Loss: 20.3802, RMSE: 44.3790, R2: 0.5168\n", + "Epoch [25/100], Loss: 20.4209, RMSE: 44.5661, R2: 0.5155\n", + "Epoch [26/100], Loss: 20.3264, RMSE: 44.5799, R2: 0.5191\n", + "Epoch [27/100], Loss: 20.0937, RMSE: 44.0491, R2: 0.5244\n", + "Epoch [28/100], Loss: 20.1104, RMSE: 44.0550, R2: 0.5228\n", + "Epoch [29/100], Loss: 19.9297, RMSE: 43.5960, R2: 0.5305\n", + "Epoch [30/100], Loss: 19.9575, RMSE: 43.7114, R2: 0.5330\n", + "Epoch [31/100], Loss: 19.8917, RMSE: 43.4068, R2: 0.5361\n", + "Epoch [32/100], Loss: 19.8369, RMSE: 43.5078, R2: 0.5380\n", + "Epoch [33/100], Loss: 19.7370, RMSE: 43.3413, R2: 0.5407\n", + "Epoch [34/100], Loss: 19.7466, RMSE: 43.2758, R2: 0.5419\n", + "Epoch [35/100], Loss: 19.6602, RMSE: 43.1147, R2: 0.5491\n", + "Epoch [36/100], Loss: 19.6093, RMSE: 43.0624, R2: 0.5459\n", + "Epoch [37/100], Loss: 19.6100, RMSE: 43.1846, R2: 0.5446\n", + "Epoch [38/100], Loss: 19.5366, RMSE: 42.9224, R2: 0.5502\n", + "Epoch [39/100], Loss: 19.6153, RMSE: 43.1783, R2: 0.5484\n", + "Epoch [40/100], Loss: 19.3696, RMSE: 42.7410, R2: 0.5594\n", + "Epoch [41/100], Loss: 19.3698, RMSE: 42.6276, R2: 0.5608\n", + "Epoch [42/100], Loss: 19.4087, RMSE: 42.7027, R2: 0.5573\n", + "Epoch [43/100], Loss: 19.2459, RMSE: 42.4314, R2: 0.5627\n", + "Epoch [44/100], Loss: 19.2574, RMSE: 42.4576, R2: 0.5622\n", + "Epoch [45/100], Loss: 19.1388, RMSE: 42.4266, R2: 0.5639\n", + "Epoch [46/100], Loss: 19.1028, RMSE: 42.1090, R2: 0.5683\n", + "Epoch [47/100], Loss: 18.9786, RMSE: 41.9760, R2: 0.5708\n", + "Epoch [48/100], Loss: 19.0496, RMSE: 42.2146, R2: 0.5687\n", + "Epoch [49/100], Loss: 19.0203, RMSE: 42.0168, R2: 0.5728\n", + "Epoch [50/100], Loss: 18.9172, RMSE: 42.0872, R2: 0.5731\n", + "Epoch [51/100], Loss: 18.7872, RMSE: 41.7375, R2: 0.5808\n", + "Epoch [52/100], Loss: 18.8261, RMSE: 41.7999, R2: 0.5793\n", + "Epoch [53/100], Loss: 18.8049, RMSE: 41.7468, R2: 0.5837\n", + "Epoch [54/100], Loss: 18.6663, RMSE: 41.4973, R2: 0.5860\n", + "Epoch [55/100], Loss: 18.6644, RMSE: 41.5084, R2: 0.5833\n", + "Epoch [56/100], Loss: 18.6205, RMSE: 41.3317, R2: 0.5901\n", + "Epoch [57/100], Loss: 18.6597, RMSE: 41.3413, R2: 0.5901\n", + "Epoch [58/100], Loss: 18.6209, RMSE: 41.3341, R2: 0.5878\n", + "Epoch [59/100], Loss: 18.5543, RMSE: 41.0878, R2: 0.5953\n", + "Epoch [60/100], Loss: 18.5161, RMSE: 41.0181, R2: 0.5968\n", + "Epoch [61/100], Loss: 18.3288, RMSE: 40.7768, R2: 0.5987\n", + "Epoch [62/100], Loss: 18.4611, RMSE: 40.9405, R2: 0.5982\n", + "Epoch [63/100], Loss: 18.3957, RMSE: 40.9182, R2: 0.5988\n", + "Epoch [64/100], Loss: 18.3633, RMSE: 40.9870, R2: 0.6015\n", + "Epoch [65/100], Loss: 18.3345, RMSE: 40.7092, R2: 0.6030\n", + "Epoch [66/100], Loss: 18.2776, RMSE: 40.6087, R2: 0.6068\n", + "Epoch [67/100], Loss: 18.2598, RMSE: 40.5746, R2: 0.6041\n", + "Epoch [68/100], Loss: 18.1481, RMSE: 40.2845, R2: 0.6092\n", + "Epoch [69/100], Loss: 18.1484, RMSE: 40.3405, R2: 0.6102\n", + "Epoch [70/100], Loss: 18.1126, RMSE: 40.3029, R2: 0.6103\n", + "Epoch [71/100], Loss: 18.1338, RMSE: 40.2659, R2: 0.6115\n", + "Epoch [72/100], Loss: 18.0561, RMSE: 39.9730, R2: 0.6153\n", + "Epoch [73/100], Loss: 18.0446, RMSE: 40.1750, R2: 0.6151\n", + "Epoch [74/100], Loss: 18.0464, RMSE: 40.1197, R2: 0.6153\n", + "Epoch [75/100], Loss: 17.9086, RMSE: 39.8035, R2: 0.6182\n", + "Epoch [76/100], Loss: 17.9980, RMSE: 39.8714, R2: 0.6164\n", + "Epoch [77/100], Loss: 17.9506, RMSE: 40.0059, R2: 0.6154\n", + "Epoch [78/100], Loss: 17.8882, RMSE: 39.4674, R2: 0.6195\n", + "Epoch [79/100], Loss: 17.8874, RMSE: 39.4358, R2: 0.6234\n", + "Epoch [80/100], Loss: 17.8933, RMSE: 39.4258, R2: 0.6204\n", + "Epoch [81/100], Loss: 17.8422, RMSE: 39.3291, R2: 0.6266\n", + "Epoch [82/100], Loss: 17.9011, RMSE: 39.4475, R2: 0.6224\n", + "Epoch [83/100], Loss: 17.8287, RMSE: 39.1086, R2: 0.6269\n", + "Epoch [84/100], Loss: 17.7919, RMSE: 39.2602, R2: 0.6253\n", + "Epoch [85/100], Loss: 17.7529, RMSE: 39.2429, R2: 0.6279\n", + "Epoch [86/100], Loss: 17.6680, RMSE: 38.7648, R2: 0.6325\n", + "Epoch [87/100], Loss: 17.7104, RMSE: 38.9088, R2: 0.6288\n", + "Epoch [88/100], Loss: 17.7814, RMSE: 38.9990, R2: 0.6311\n", + "Epoch [89/100], Loss: 17.6855, RMSE: 38.6856, R2: 0.6347\n", + "Epoch [90/100], Loss: 17.6905, RMSE: 38.8867, R2: 0.6332\n", + "Epoch [91/100], Loss: 17.6083, RMSE: 38.6006, R2: 0.6336\n", + "Epoch [92/100], Loss: 17.5815, RMSE: 38.3766, R2: 0.6360\n", + "Epoch [93/100], Loss: 17.5437, RMSE: 38.6112, R2: 0.6369\n", + "Epoch [94/100], Loss: 17.5988, RMSE: 38.6639, R2: 0.6370\n", + "Epoch [95/100], Loss: 17.5572, RMSE: 38.5148, R2: 0.6362\n", + "Epoch [96/100], Loss: 17.5333, RMSE: 38.5656, R2: 0.6362\n", + "Epoch [97/100], Loss: 17.4629, RMSE: 38.1411, R2: 0.6442\n", + "Epoch [98/100], Loss: 17.5057, RMSE: 38.3545, R2: 0.6371\n", + "Epoch [99/100], Loss: 17.4759, RMSE: 38.2431, R2: 0.6395\n", + "Epoch [100/100], Loss: 17.5492, RMSE: 38.4732, R2: 0.6391\n" + ] + } + ], + "source": [ + "from sklearn.metrics import root_mean_squared_error, r2_score\n", + "import numpy as np\n", + "# 训练参数\n", + "num_epochs = 100\n", + "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n", + "model.to(device)\n", + "\n", + "# 初始化指标列表\n", + "train_losses = []\n", + "train_rmse = []\n", + "train_r2 = []\n", + "\n", + "# 训练循环\n", + "for epoch in range(num_epochs):\n", + " model.train()\n", + " total_loss = 0\n", + " all_preds = []\n", + " all_targets = []\n", + " \n", + " for batch_x, batch_y in train_loader:\n", + " # 移动数据到设备\n", + " batch_x, batch_y = batch_x.to(device), batch_y.to(device)\n", + " # 前向传播\n", + " outputs = model(batch_x)\n", + " # 计算损失\n", + " loss = criterion(outputs, batch_y)\n", + " # 反向传播和优化\n", + " optimizer.zero_grad()\n", + " loss.backward()\n", + " optimizer.step()\n", + " total_loss += loss.item()\n", + " # 收集预测和真实值\n", + " all_preds.append(outputs.cpu().detach().numpy())\n", + " all_targets.append(batch_y.cpu().detach().numpy())\n", + " \n", + " train_losses.append(total_loss / len(train_loader))\n", + " \n", + " # 更新学习率\n", + " scheduler.step() # 在每个epoch结束后调整学习率\n", + " # 计算RMSE和R²\n", + " all_preds = np.concatenate(all_preds, axis=0)\n", + " all_targets = np.concatenate(all_targets, axis=0)\n", + " epoch_rmse = root_mean_squared_error(all_targets, all_preds)\n", + " epoch_r2 = r2_score(all_targets, all_preds)\n", + " train_rmse.append(epoch_rmse)\n", + " train_r2.append(epoch_r2)\n", + " \n", + " # 打印每轮训练的平均损失和指标\n", + " print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {train_losses[-1]:.4f}, RMSE: {epoch_rmse:.4f}, R2: {epoch_r2:.4f}')\n", + " if epoch % 20 == 0: torch.save(model.state_dict(), f'models/now.pth')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "b78485253c6baa0c", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-03T08:44:35.053096Z", + "start_time": "2024-12-03T08:44:33.707752Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Average Test Loss: 16.3081\n", + "Indicator 1 (pH.in.CaCl2) - RMSE: 0.8083, R²: 0.7902\n", + "Indicator 2 (pH.in.H2O) - RMSE: 0.7884, R²: 0.7881\n", + "Indicator 3 (OC) - RMSE: 5.2944, R²: 0.8994\n", + "Indicator 4 (CaCO3) - RMSE: 6.1148, R²: 0.9063\n", + "Indicator 5 (N) - RMSE: 1.2336, R²: 0.8161\n", + "Indicator 6 (P) - RMSE: 5.8376, R²: 0.1469\n", + "Indicator 7 (K) - RMSE: 12.5448, R²: 0.3947\n", + "Indicator 8 (CEC) - RMSE: 2.8166, R²: 0.6321\n" + ] + } + ], + "source": [ + "from sklearn.metrics import root_mean_squared_error, r2_score\n", + "\n", + "# 模型评估\n", + "model.eval()\n", + "total_test_loss = 0\n", + "test_preds = []\n", + "test_targets = []\n", + "\n", + "# 将列名和索引建立映射\n", + "target_columns = ['pH.in.CaCl2', 'pH.in.H2O', 'OC', 'CaCO3', 'N', 'P', 'K', 'CEC']\n", + "column_mapping = {i: col for i, col in enumerate(target_columns)}\n", + "\n", + "with torch.no_grad():\n", + " for batch_x, batch_y in test_loader:\n", + " batch_x, batch_y = batch_x.to(device), batch_y.to(device)\n", + " test_outputs = model(batch_x)\n", + " test_loss = criterion(test_outputs, batch_y)\n", + " total_test_loss += test_loss.item()\n", + "\n", + " # 收集预测值和真实值\n", + " test_preds.append(test_outputs.cpu().numpy())\n", + " test_targets.append(batch_y.cpu().numpy())\n", + "\n", + " # 计算平均测试损失\n", + " avg_test_loss = total_test_loss / len(test_loader)\n", + " print(f'Average Test Loss: {avg_test_loss:.4f}')\n", + "\n", + " # 将预测值和真实值拼接在一起\n", + " test_preds = np.concatenate(test_preds, axis=0)\n", + " test_targets = np.concatenate(test_targets, axis=0)\n", + "\n", + " # 计算每个指标的 RMSE 和 R²\n", + " for i in range(test_targets.shape[1]):\n", + " target_i = test_targets[:, i]\n", + " pred_i = test_preds[:, i]\n", + "\n", + " # 计算 RMSE 和 R²\n", + " rmse_i = np.sqrt(root_mean_squared_error(target_i, pred_i))\n", + " r2_i = r2_score(target_i, pred_i)\n", + "\n", + " # 打印当前指标的结果\n", + " print(f'Indicator {i + 1} ({column_mapping[i]}) - RMSE: {rmse_i:.4f}, R²: {r2_i:.4f}')\n", + "# 可选:保存模型\n", + "#torch.save(model.state_dict(), 'models/resnet1d50_new_model.pth')" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "77a1686273c1342f", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-03T08:44:53.818427Z", + "start_time": "2024-12-03T08:44:53.662753Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAGGCAYAAACqvTJ0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/GU6VOAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3iT5foH8G92d0uhpYWWvUGG4CgqoKAsPSr83CiIx4GoR9Cj4kBxoec4zxFxHAREERcogoiAgIAs2bJ3C3SwukfS5P39EfI240nyNEmbFr6f6+qlffKO500CvLlz3/ejURRFARERERERERERUS3ShnsCRERERERERER04WFQioiIiIiIiIiIah2DUkREREREREREVOsYlCIiIiIiIiIiolrHoBQREREREREREdU6BqWIiIiIiIiIiKjWMShFRERERERERES1jkEpIiIiIiIiIiKqdQxKERERERERERFRrWNQiojqpFGjRqFFixYB7fvSSy9Bo9GEdkJEREREtYz3Q6EVzPNJRDWDQSkiqhaNRiP1s2LFinBPNSxGjRqFmJiYcE+DiIiIahDvh3wbNWoUNBoN4uLiUFZW5vH4/v371eforbfeqvbxS0tL8dJLL12wzy/R+UQf7gkQUf0ya9Ysl98///xzLFmyxGO8Y8eOQZ3n008/hc1mC2jf559/Hs8880xQ5yciIiLyhvdD/un1epSWluKnn37Crbfe6vLYl19+iYiICJSXlwd07NLSUkyaNAkA0K9fP+n9gnk+iahmMChFRNUyYsQIl9/XrVuHJUuWeIy7Ky0tRVRUlPR5DAZDQPMD7DdBej3/eiMiIqKawfsh/0wmE6644gp89dVXHkGp2bNnY+jQofj+++9rZS4lJSWIjo4O6vkkoprB8j0iCrl+/fqhS5cu2LRpE/r06YOoqCg8++yzAIAff/wRQ4cORZMmTWAymdC6dWu88sorsFqtLsdwr/k/cuSImuL9ySefoHXr1jCZTLjkkkuwceNGl31FPRQ0Gg0eeeQR/PDDD+jSpQtMJhM6d+6MX375xWP+K1asQK9evRAREYHWrVvj448/Dnlfhm+//RY9e/ZEZGQkGjVqhBEjRuD48eMu2+Tk5ODee+9FWloaTCYTUlNTceONN+LIkSPqNn/++ScGDhyIRo0aITIyEi1btsTo0aNDNk8iIiIKDO+HgDvvvBOLFi1Cfn6+OrZx40bs378fd955p3Cf/Px8PP7440hPT4fJZEKbNm3w5ptvqhlOR44cQVJSEgBg0qRJahngSy+9pD5nMTExOHjwIIYMGYLY2FjcddddwucTAGw2G95//31cdNFFiIiIQFJSEgYNGoQ///xT3WbJkiW48sorkZCQgJiYGLRv3159LYkoOEwlIKIacfr0aQwePBi33347RowYgcaNGwMAZsyYgZiYGIwfPx4xMTH47bffMHHiRBQWFuLf//633+POnj0bRUVFePDBB6HRaPCvf/0Lw4YNw6FDh/x++7V69WrMnTsXDz/8MGJjY/Gf//wHw4cPR2ZmJho2bAgA2LJlCwYNGoTU1FRMmjQJVqsVL7/8snrzEwozZszAvffei0suuQSTJ09Gbm4u3n//faxZswZbtmxBQkICAGD48OHYuXMnHn30UbRo0QJ5eXlYsmQJMjMz1d+vu+46JCUl4ZlnnkFCQgKOHDmCuXPnhmyuREREFLgL/X5o2LBheOihhzB37lz1S7PZs2ejQ4cOuPjiiz22Ly0tRd++fXH8+HE8+OCDaNasGf744w9MmDAB2dnZeO+995CUlISpU6dizJgxuPnmmzFs2DAAQNeuXdXjVFZWYuDAgbjyyivx1ltv+cxOu++++zBjxgwMHjwYf//731FZWYlVq1Zh3bp16NWrF3bu3Inrr78eXbt2xcsvvwyTyYQDBw5gzZo11XouiMgLhYgoCGPHjlXc/yrp27evAkD56KOPPLYvLS31GHvwwQeVqKgopby8XB0bOXKk0rx5c/X3w4cPKwCUhg0bKmfOnFHHf/zxRwWA8tNPP6ljL774osecAChGo1E5cOCAOrZt2zYFgPLf//5XHbvhhhuUqKgo5fjx4+rY/v37Fb1e73FMkZEjRyrR0dFeHzebzUpycrLSpUsXpaysTB1fsGCBAkCZOHGioiiKcvbsWQWA8u9//9vrsebNm6cAUDZu3Oh3XkRERFRzeD/kyvl+6P/+7/+U/v37K4qiKFarVUlJSVEmTZqkXovzvc4rr7yiREdHK/v27XM53jPPPKPodDolMzNTURRFOXnypAJAefHFF4XnBqA888wzwsecn8/ffvtNAaA89thjHtvabDZFURTl3XffVQAoJ0+e9HvdRFR9LN8johphMplw7733eoxHRkaq/19UVIRTp07hqquuQmlpKfbs2eP3uLfddhsaNGig/n7VVVcBAA4dOuR33wEDBqB169bq7127dkVcXJy6r9VqxdKlS3HTTTehSZMm6nZt2rTB4MGD/R5fxp9//om8vDw8/PDDiIiIUMeHDh2KDh06YOHChQDsz5PRaMSKFStw9uxZ4bEcGVULFiyAxWIJyfyIiIgodHg/ZC/hW7FiBXJycvDbb78hJyfHa+net99+i6uuugoNGjTAqVOn1J8BAwbAarXi999/lz7vmDFj/G7z/fffQ6PR4MUXX/R4zFGm6Ljf+vHHH9kknagGMChFRDWiadOmMBqNHuM7d+7EzTffjPj4eMTFxSEpKUltClpQUOD3uM2aNXP53XFD5i1w42tfx/6OffPy8lBWVoY2bdp4bCcaC8TRo0cBAO3bt/d4rEOHDurjJpMJb775JhYtWoTGjRujT58++Ne//oWcnBx1+759+2L48OGYNGkSGjVqhBtvvBHTp09HRUVFSOZKREREweH9ENS+Tl9//TW+/PJLXHLJJV6Ps3//fvzyyy9ISkpy+RkwYIA6Nxl6vR5paWl+tzt48CCaNGmCxMREr9vcdtttuOKKK/D3v/8djRs3xu23345vvvmGASqiEGFPKSKqEc7fADrk5+ejb9++iIuLw8svv4zWrVsjIiICmzdvxtNPPy31j7tOpxOOK4pSo/uGw+OPP44bbrgBP/zwAxYvXowXXngBkydPxm+//YYePXpAo9Hgu+++w7p16/DTTz9h8eLFGD16NN5++22sW7cOMTEx4b4EIiKiCxrvh+xftA0bNgwzZ87EoUOH1IbkIjabDddeey2eeuop4ePt2rWTPqdWG5r8i8jISPz+++9Yvnw5Fi5ciF9++QVff/01rrnmGvz6669en08iksOgFBHVmhUrVuD06dOYO3cu+vTpo44fPnw4jLOqkpycjIiICBw4cMDjMdFYIJo3bw4A2Lt3L6655hqXx/bu3as+7tC6dWs88cQTeOKJJ7B//350794db7/9Nr744gt1m8svvxyXX345XnvtNcyePRt33XUX5syZg7///e8hmTMRERGFzoV4P3TnnXfis88+g1arxe233+51u9atW6O4uFjNjPImVCsit27dGosXL8aZM2d8ZktptVr0798f/fv3xzvvvIPXX38dzz33HJYvX+53rkTkG8v3iKjWOL5Jcv4mzmw248MP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/BPz58/H2rVroSgKhgwZot7Ajh07FhUVFfj999+xY8cOvPnmm2o22QsvvIBdu3Zh0aJF2L17N6ZOnYpGjRoFNZ+6xGazYfr06Rg5cqRLtlx8fDzuu+8+jB8/HsuXL8emTZtw7733IiMjo06tvBcXYUDrpGgAzJYiovNHfbvXGD16tMsXNp999hnuvfdej+0mT56Mzz//HB999BF27tyJcePGYcSIEVi5ciUA+79JaWlp+Pbbb7Fr1y5MnDgRzz77LL755huX4yxfvhwHDx7E8uXLMXPmTMyYMSPg0jtnpaWlePPNN/G///0PO3fuRHJyMoqKijBy5EisXr0a69atQ9u2bTFkyBCXgJHIc889hyeffBJbt25Fu3btcMcdd6CystLr9gcPHsQPP/yABQsWYMGCBVi5cqVLOd348eOxZs0azJ8/H0uWLMGqVas8gmP+5OXlYd68edDpdNDpdC6PGY1G3HXXXS6v44wZMzB69GiX7ZKSkqDVavHdd9/BarVW6/w1jeV7IVZptYV7CkREJFBmsaLTxMVhOfeulwciyhiaf3JffvllXHvtterviYmJ6Natm/r7K6+8gnnz5mH+/Pl45JFHvB5n1KhRuOOOOwAAr7/+Ov7zn/9gw4YNHunaDhaLBR999BFat24NAHjkkUfw8ssvq4//97//xYQJE3DzzTcDAD744AM1aykQ+/fvx/z587FmzRr07t0bAPDll18iPT0dP/zwA2655RZkZmZi+PDhuOiiiwAArVq1UvfPzMxEjx490KtXLwD2bLHzydKlS5GZmelx0wkA7777LrRaLYYPH46KigoMHDgQH374YRhm6Vu39AQcPFmCrVkFuKZD43BPh4goaPXtXmPEiBGYMGECjh61L9K1Zs0azJkzBytWrFC3qaiowOuvv46lS5eqGbetWrXC6tWr8fHHH6Nv374wGAyYNGmSuk/Lli2xdu1afPPNN7j11lvV8QYNGuCDDz6ATqdDhw4dMHToUCxbtgz3339/EFduv0f58MMPXe6HrrnmGpdtPvnkEyQkJGDlypW4/vrrvR7rySefVDOQJ02ahM6dO+PAgQPo0KGDcHubzYYZM2YgNjYWAHD33Xdj2bJleO2111BUVISZM2di9uzZ6N+/PwB7lrNMH8iCggLExMTYA52lpQCAxx57DNHR0R7bjh49GldddRXef/99bNq0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3HPPPfWyZQWDUiHGRudERERE1RNh0KFDSiwAYN6W41AU3k8REdWk9PR0zJkzB2VlZepYeXk5Zs+ejWbNmoVxZq7S09ORnZ2t/jzxxBPo3Lmzy9htt92mbm+1WqVX1E1JSYHJZJKeS2RkJJKTk6t9DYGIi4tDdnY2jh8/ju+//x579+7FLbfcoj5eWlqKzZs344UXXsDmzZsxd+5c7N27F3/7299qZX6hxKBUiFXaeBNFREREVF0DO6cAAP69eC8e/WoLCsosYZ4REdH56+KLL0Z6ejrmzp2rjs2dOxfNmjVDjx49XLa12WyYPHkyWrZsicjISHTr1g3fffed+rjVasV9992nPt6+fXu8//77LscYNWoUbrrpJrz11ltITU1Fw4YNMXbsWFgsvv+u1+l0SElJUX9iYmKg1+vV33/55RekpqZi/vz56NSpE0wmEzIzM7Fx40Zce+21aNSoEeLj49G3b19s3rzZ5dgajQY//PADgKqMrLlz5+Lqq69GVFQUunXrhrVr16rbz5gxAwkJCervL730Erp3745Zs2ahRYsWiI+Px+23346ioiJ1m6KiItx1112Ijo5Gamoq3n33XfTr1w+PP/64z+vWaDRISUlBamoqevfujfvuuw8bNmxAYWEhACA+Ph5LlizBrbfeivbt2+Pyyy/HBx98gE2bNrlkVNUHDEoRERERUdiNvboN/jmwPXRaDRZsz8aQ91dh09Ez4Z4WEZE8RQHMJeH5CSDDdPTo0Zg+fbr6+2effYZ7773XY7vJkyfj888/x0cffYSdO3di3LhxGDFiBFauXAnAHrRKS0vDt99+i127dmHixIl49tln8c0337gcZ/ny5Th48CCWL1+OmTNnYsaMGQGX3jkrLS3Fm2++if/973/YuXMnkpOTUVRUhJEjR2L16tVYt24d2rZtiyFDhrgEjESee+45PPnkk9i6dSvatWuHO+64A5WVlV63P3jwIH744QcsWLAACxYswMqVK/HGG2+oj48fPx5r1qzB/PnzsWTJEqxatcojOOZPXl4e5s2bB51OB51O53W7goICaDQal8BZfcBG50REREQUdjqtBmOvboPerRviH3O2IvNMKW79eB0evaYN7r+qFaJNvG0lojrOUgq83iQ85372BGCMrtYuI0aMwIQJE3D06FEAwJo1azBnzhysWLFC3aaiogKvv/46li5d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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "\n", + "\n", + "\n", + "plt.figure(figsize=(12, 4))\n", + "plt.subplot(1, 2, 1)\n", + "train_losses = np.array(train_losses)\n", + "train_losses = train_losses[train_losses != 0]\n", + "plt.plot(train_losses, label='Training Loss')\n", + "plt.xlabel('Epoch')\n", + "plt.ylabel('Loss')\n", + "plt.title('Training Loss')\n", + "plt.legend()\n", + "\n", + "plt.subplot(1, 2, 2)\n", + "plt.plot(train_rmse, label='Mean_Training RMSE')\n", + "plt.plot(train_r2, label='Mean_Training R2')\n", + "plt.xlabel('Epoch')\n", + "plt.ylabel('Metric')\n", + "plt.title('Training Metrics')\n", + "plt.legend()\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "3.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/test.py b/test.py new file mode 100644 index 0000000000000000000000000000000000000000..8dccf0a9499b49404f62dff4186edd0ad6e4f021 --- /dev/null +++ b/test.py @@ -0,0 +1,219 @@ +import torch.nn as nn +import torch +from matplotlib import pyplot as plt +from torch.utils.data import DataLoader +from data_load import load_soil_data +from data_processing import process_spectra +from data_processing import preprocess_with_downsampling +from resnet1d_multitask import ResNet1D_MultiTask,get_model + +bin_sizes = [5,10,15,20] # 不同的降采样窗口大小 +# 预处理数据 +methods = ['Abs-SG0', 'Abs-SG0-SNV', 'Abs-SG1', 'Abs-SG1-SNV', 'Abs-SG2', 'Abs-SG2-SNV'] +# 定义目标列 +target_columns = ['pH.in.CaCl2', 'pH.in.H2O', 'OC', 'CaCO3', 'N', 'P', 'K', 'CEC'] + + +for j in len(bin_sizes): + # 加载数据 + X_train, X_test, y_train, y_test, wavelengths = load_soil_data('../LUCAS.2009_abs.csv', target_columns) + + # 确保数据形状为 (n_samples, n_wavelengths) + X_train, X_test = X_train.squeeze(), X_test.squeeze() + + X_train= process_spectra(X_train,methods[5]) + X_test = process_spectra(X_test,methods[5]) + + X_train,X_train_nwavelengths=preprocess_with_downsampling(X_train,wavelengths,bin_sizes[j]) + X_test,X_test_nwavelengths=preprocess_with_downsampling(X_test,wavelengths,bin_sizes[j]) + # 将数据形状调整为 (n_samples, 1, n_wavelengths) + X_train = X_train.reshape(X_train.shape[0], 1, X_train.shape[1]) + X_test = X_test.reshape(X_test.shape[0], 1, X_test.shape[1]) + + # 检查数据形状 + print("X_train shape:", X_train.shape) + print("y_train shape:", y_train.shape) + print("X_test shape:", X_test.shape) + print("y_test shape:", y_test.shape) + assert X_train.shape[0] == y_train.shape[0], "Mismatch in number of samples between X_train and y_train" + assert X_test.shape[0] == y_test.shape[0], "Mismatch in number of samples between X_test and y_test" + + # 创建数据加载器 + train_dataset = torch.utils.data.TensorDataset(torch.tensor(X_train, dtype=torch.float32), torch.tensor(y_train, dtype=torch.float32)) + test_dataset = torch.utils.data.TensorDataset(torch.tensor(X_test, dtype=torch.float32), torch.tensor(y_test, dtype=torch.float32)) + + train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True) + test_loader = DataLoader(test_dataset, batch_size=256, shuffle=False) + + + # 模型参数设置 + model_name = 'C' # 可以改为 'A' 或 'B' + model = get_model(model_name) + # 损失函数 + criterion = nn.SmoothL1Loss() + # 优化器 + optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-5) + scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.81) + + + + from sklearn.metrics import root_mean_squared_error, r2_score + import numpy as np + # 训练参数 + num_epochs = 50 + device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + model.to(device) + + # 初始化指标列表 + train_losses = [] + test_losses = [] + train_rmse = [] + train_r2 = [] + + # 训练循环 + for epoch in range(num_epochs): + model.train() + total_loss = 0 + all_preds = [] + all_targets = [] + + for batch_x, batch_y in train_loader: + # 移动数据到设备 + batch_x, batch_y = batch_x.to(device), batch_y.to(device) + # 前向传播 + outputs = model(batch_x) + # 计算损失 + loss = criterion(outputs, batch_y) + # 反向传播和优化 + optimizer.zero_grad() + loss.backward() + optimizer.step() + total_loss += loss.item() + # 收集预测和真实值 + all_preds.append(outputs.cpu().detach().numpy()) + all_targets.append(batch_y.cpu().detach().numpy()) + + train_losses.append(total_loss / len(train_loader)) + + # 更新学习率 + scheduler.step() # 在每个epoch结束后调整学习率 + # 计算RMSE和R² + all_preds = np.concatenate(all_preds, axis=0) + all_targets = np.concatenate(all_targets, axis=0) + epoch_rmse = root_mean_squared_error(all_targets, all_preds) + epoch_r2 = r2_score(all_targets, all_preds) + train_rmse.append(epoch_rmse) + train_r2.append(epoch_r2) + + # 在每个epoch结束后评估测试集 + model.eval() + test_loss = 0 + with torch.no_grad(): + for batch_x, batch_y in test_loader: + batch_x, batch_y = batch_x.to(device), batch_y.to(device) + test_outputs = model(batch_x) + loss = criterion(test_outputs, batch_y) + test_loss += loss.item() + test_losses.append(test_loss / len(test_loader)) + + print(f'Epoch [{epoch+1}/{num_epochs}], Train Loss: {train_losses[-1]:.4f}, Test Loss: {test_losses[-1]:.4f}') + #if epoch % 20 == 0: torch.save(model.state_dict(), f'models/now.pth') + + + + + + + from sklearn.metrics import root_mean_squared_error, r2_score + # 模型评估 + model.eval() + total_test_loss = 0 + test_preds = [] + test_targets = [] + + # 将列名和索引建立映射 + target_columns = ['pH.in.CaCl2', 'pH.in.H2O', 'OC', 'CaCO3', 'N', 'P', 'K', 'CEC'] + column_mapping = {i: col for i, col in enumerate(target_columns)} + + with torch.no_grad(): + for batch_x, batch_y in test_loader: + batch_x, batch_y = batch_x.to(device), batch_y.to(device) + test_outputs = model(batch_x) + test_loss = criterion(test_outputs, batch_y) + total_test_loss += test_loss.item() + + # 收集预测值和真实值 + test_preds.append(test_outputs.cpu().numpy()) + test_targets.append(batch_y.cpu().numpy()) + + # 计算平均测试损失 + avg_test_loss = total_test_loss / len(test_loader) + print(f'Average Test Loss: {avg_test_loss:.4f}') + + # 将预测值和真实值拼接在一起 + test_preds = np.concatenate(test_preds, axis=0) + test_targets = np.concatenate(test_targets, axis=0) + + # 计算每个指标的 RMSE 和 R² + from datetime import datetime + current_time = datetime.now().strftime("%Y%m%d_%H%M%S") + results_file = f'../results3/metrics_model{model_name}_{current_time}.txt' + + # 确保results目录存在 + import os + if not os.path.exists('../results3'): + os.makedirs('../results3') + + with open(results_file, 'w') as f: + f.write(f"Results for Model {model_name} generated at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") + f.write("-" * 50 + "\n") + for i in range(test_targets.shape[1]): + target_i = test_targets[:, i] + pred_i = test_preds[:, i] + + # 计算 RMSE 和 R² + rmse_i = np.sqrt(root_mean_squared_error(target_i, pred_i)) + r2_i = r2_score(target_i, pred_i) + + # 打印当前指标的结果 + result_line = f'Indicator {i + 1} ({column_mapping[i]}) - RMSE: {rmse_i:.4f}, R²: {r2_i:.4f}' + print(result_line) + #f.write(result_line + '\n') + + #f.write("\nAverage Test Loss: {:.4f}\n".format(avg_test_loss)) + + # 绘制并保存训练和测试损失图 + plt.figure(figsize=(10, 6)) + plt.plot(train_losses, label='Training Loss') + plt.plot(test_losses, label='Test Loss') + plt.xlabel('Epoch') + plt.ylabel('Loss') + plt.title(f'Training and Test Loss over Epochs (Model {model_name})') + plt.legend() + plt.grid(True) + plt.savefig(f'../results3/loss_curves_model{model_name}_{current_time}_{bin_sizes[j]}.png', dpi=300, bbox_inches='tight') + plt.show() + + # 绘制并保存指标图 + plt.figure(figsize=(12, 4)) + plt.subplot(1, 2, 1) + plt.plot(train_rmse, label='Mean_Training RMSE') + plt.plot(train_r2, label='Mean_Training R2') + plt.xlabel('Epoch') + plt.ylabel('Metric') + plt.title(f'Training Metrics (Model {model_name})') + plt.legend() + + plt.subplot(1, 2, 2) + plt.plot(test_losses, label='Test Loss') + plt.xlabel('Epoch') + plt.ylabel('Loss') + plt.title(f'Test Loss (Model {model_name})') + plt.legend() + + plt.tight_layout() + plt.savefig(f'../results3/training_metrics_model{model_name}_{current_time}_{bin_sizes[j]}.png', dpi=300, bbox_inches='tight') + plt.show() + + print(f"\nResults have been saved to: {results_file}") + print(f"Figures have been saved to: ../results3/") \ No newline at end of file