使用PCA减少CNN训练集的维度

简短版本: 我使用PCA来减少训练数据的维数时遇到了困难。我的训练数据是为一个二维CNN准备的,用于将图形图像分类为三个类别。

模型的目的

我对主成分分析(PCA)还不熟悉。我有一个二维卷积神经网络(CNN),用于将36×36像素的图形图像分类为三个类别,如下所示:enter image description here

改进模型

我发现大部分像素都是白色的,因此CNN效率很低,训练时间很长。我了解到降维技术,并尝试使用PCA。我将其中一张训练图像转换为灰度,并可视化了“特征图”(如左图所示)。然后我从特征图重建了原图(如右图所示)。

X=grayscale pca_oliv = PCA(n_components = 36)X_proj = pca_oliv.fit_transform(X)print(np.cumsum(pca_oliv.explained_variance_ratio_))plt.imshow(np.reshape(pca_oliv.components_, (36,36)), cmap=plt.cm.bone, interpolation='nearest')

enter image description here enter image description here

问题

但我知道它可以做得更好。 这是使用n=36维的情况。通过绘制解释方差,我发现拐点在3维。这意味着只用36维中的3维,就可以保留91.7%的方差。

enter image description here

但如果我将pca_oliv = PCA(n_components = 36)改为pca_oliv = PCA(n_components = 3),一切都会变得混乱:ValueError: cannot reshape array of size 108 into shape (36,36)。为什么会这样?我做错了什么?

最小工作示例

pip install tensorflowpip install numpypip install matplotlib"""# 导入库"""# 导入库import tensorflow as tffrom tensorflow import kerasfrom keras.models import Sequentialfrom keras.layers import Dense, Flatten, Conv2D, MaxPooling2D, Dropoutfrom tensorflow.keras import layersfrom tensorflow.keras.utils import to_categoricalimport numpy as npimport matplotlib.pyplot as pltplt.style.use('fivethirtyeight')"""# 加载数据集"""import pathlibdataset_url = "*/TrainingSet.tar.gz"data_dir = tf.keras.utils.get_file(origin = dataset_url,                                   fname = "TrainingSet",                                   untar = True)data_dir = pathlib.Path(data_dir)"""# 显示#图像以检查"""print(list(data_dir.glob('*/*.png')))image_count = len(list(data_dir.glob('*/*.png')))print(image_count)"""# 显示示例图像"""pip install sklearnimport numpy as npimport osimport PILimport PIL.Imageimport tensorflow as tfimport tensorflow_datasets as tfdsfrom sklearn.decomposition import PCAgraphs = list(data_dir.glob('*/*.png'))PIL.Image.open(str(graphs[6]))"""# 定义图像尺寸和批量大小"""batch_size = 32img_height = 36img_width = 36"""# 创建训练和验证集(80%,20%)"""train_ds = tf.keras.preprocessing.image_dataset_from_directory(  data_dir,  validation_split=0.2,  subset="training",  seed=123,  image_size=(img_height, img_width),  batch_size=batch_size)val_ds = tf.keras.preprocessing.image_dataset_from_directory(  data_dir,  validation_split=0.2,  subset="validation",  seed=123,  image_size=(img_height, img_width),  batch_size=batch_size)"""# 定义3个类别"""class_names = ['Cubic Sinusoidal', 'Linear Sinusoidal', 'Quadratic Sinusoidal']print(class_names)"""# 监督学习(来自训练集的9个样本)"""!pip install skimagefrom skimage import datafrom skimage.color import rgb2grayimport matplotlib.pyplot as pltsubGraphs = []plt.figure(figsize=(10, 10))for images, labels in train_ds.take(1):  for i in range(9):    ax = plt.subplot(3, 3, i + 1)    plt.imshow(images[i].numpy().astype("uint8"))    subGraphs.append(images[i].numpy().astype("uint8"))    plt.title(class_names[labels[i]])    plt.axis("off")subGraphs = np.array(subGraphs)print(subGraphs.shape)grayscale = rgb2gray(subGraphs[1])print(grayscale.shape)X=grayscale pca_oliv = PCA(n_components = 36)X_proj = pca_oliv.fit_transform(X)print(np.cumsum(pca_oliv.explained_variance_ratio_))plt.plot(np.cumsum(pca_oliv.explained_variance_ratio_))plt.imshow(np.reshape(pca_oliv.components_, (36,36)), cmap=plt.cm.bone, interpolation='nearest')X_inv_proj = pca_oliv.inverse_transform(X_proj)X_proj_img = np.reshape(X_inv_proj,(1,36,36))plt.imshow(X_proj_img[0], cmap=plt.cm.bone, interpolation='nearest')

供参考,这是我的Jupyter笔记本:PCA+CNN。如果有人能帮忙,那就太好了。


回答:

PCA 用于减少维数,同时确保低维表示能够覆盖尽可能多的变化。现在,想想你最初有多少维度。我看到你使用了(36, 36)的灰度图像。这里,每个像素都是你原始的特征。你又选取了9张图像来对它们应用PCA

在这种情况下,你的样本数量少于原始特征数量,即9 < 36*36,因此,你只需要不超过9个主成分就能覆盖全部方差。但如果你的样本数量大于特征数量(36*36 = 1296),你就可以设置更大的n_components值。参见这里,sklearn.decomposition.PCA为什么对于n个数据,如果维数≥n,只有n−1个主成分?

但无论如何,我不会深入探讨PCA的细节,而是描述你需要在代码中做哪些更改。

grayscale = rgb2gray(subGraphs)print(grayscale.shape)grayscale = grayscale.reshape((grayscale.shape[0], grayscale.shape[1] * grayscale.shape[2]))print(grayscale.shape)

由于PCA期望输入形状为(样本数量, 特征数量),因此,你需要将样本数量保持在第一维度,第二维度将是所有像素值(原始特征)。如果你使用了彩色图像,那么你需要将所有通道的特征包含在同一第二维度中,类似于这样:

color_img = color_img.reshape((color_img.shape[0], color_img.shape[1] * color_img.shape[2] * color_img.shape[3]))print(color_img.shape)

现在你可以应用PCA

X=grayscale pca_oliv = PCA(n_components = 9)X_proj = pca_oliv.fit_transform(X)print(np.cumsum(pca_oliv.explained_variance_ratio_))plt.plot(np.cumsum(pca_oliv.explained_variance_ratio_))

请注意,你无法将n_components设置为大于9,因为你只使用了9张图像。如果你查看X_proj的形状,你会发现它的形状是(9, 9)。第一个9是样本数量,第二个9是每个样本在低维空间中的表示,该空间有9维(n_components)。

最后,进行逆变换以恢复原始维度(这只是为了说明目的,你将使用X_proj作为低维表示来训练模型):

X_inv_proj = pca_oliv.inverse_transform(X_proj)print(X_inv_proj.shape)for index in range(len(X_inv_proj)): # 9                      X_proj_img = np.reshape(X_inv_proj[index],(36,36))  plt.imshow(X_proj_img, cmap=plt.cm.bone, interpolation='nearest')  plt.show()

再重复一遍,X_proj包含你的9个样本的低维表示(9维)。因为它不是图像,所以你不需要重新塑形。你可以直接使用它来训练模型,就好像这9个特征代表了你原始的36*36特征一样。

这里请注意,逆变换并不是总是无损变换。在你的案例中,我们采用了9个主成分(这是可能的最大值)。所以,我们在获取PCA时实际上已经采用了100%的变化,因此,当我们应用逆变换时,它将恢复100%的变化,即恢复原始数据。但如果我们将n_components设为更低的值,那么逆变换将无法完全恢复原始信息,尽管X_inv_proj的形状不会改变,但它所包含的信息将不是原始数据的完整信息。

Related Posts

使用LSTM在Python中预测未来值

这段代码可以预测指定股票的当前日期之前的值,但不能预测…

如何在gensim的word2vec模型中查找双词组的相似性

我有一个word2vec模型,假设我使用的是googl…

dask_xgboost.predict 可以工作但无法显示 – 数据必须是一维的

我试图使用 XGBoost 创建模型。 看起来我成功地…

ML Tuning – Cross Validation in Spark

我在https://spark.apache.org/…

如何在React JS中使用fetch从REST API获取预测

我正在开发一个应用程序,其中Flask REST AP…

如何分析ML.NET中多类分类预测得分数组?

我在ML.NET中创建了一个多类分类项目。该项目可以对…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注