heatmap热力图

文章介绍了如何使用Python的Seaborn库创建和自定义热力图,包括设置颜色范围、颜色映射、数值注释、数据格式化、边框样式以及颜色带的显示方式。示例展示了如何分析航班乘客数据,并通过热力图观察时间趋势和季节性变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

heatmap 热力图

热力图在实际中常用于展示一组变量的相关系数矩阵,在展示列联表的数据分布上也有较大的用途,通过热力图我们可以非常直观地感受到数值大小的差异状况。heatmap的API如下所示:

下面将演示这些主要参数的用法,第一件事还是先导入相关的packages。

import seaborn as sns
%matplotlib inline
sns.set(font_scale=1.5)

 次演示采用的数据集是Seaborn中内置的flights航班数据集

#导入数据集后按年月两个维度进行数据透视
data=sns.load_dataset("flights")\
        .pivot("month","year","passengers") 
data.head() 

如上图所示,dataframe中的数据代表了1949年-1960年每个月的航班乘客数量,接下来热力图就隆重登场啦!

sns.set_context({"figure.figsize":(8,8)})
sns.heatmap(data=data,square=True) 
#可以看到热力图主要展示的是二维数据的数据关系
#不同大小的值对应不同的颜色深浅

 

热力图的右侧是颜色带,上面代表了数值到颜色的映射,数值由小到大对应色彩由暗到亮。从上面的heatmap中我们可以得到两层信息,一是随着时间的推移,飞机的乘客数量是在逐步增多的,二是航班的乘坐旺季在七月和八月份。下面就具体的参数进行演示。

vmax:设置颜色带的最大值
vmin:设置颜色带的最小值
 

sns.heatmap(data=data,vmin=200,vmax=500)

 

可以看到右侧的颜色带最大最小值变了,而heatmap中颜色映射关系也会随之调整,将本图和上面的图进行对比便一目了然。

cmap:设置颜色带的色系

sns.heatmap(data=data,cmap="RdBu_r")

 

好像变好看了?

center:设置颜色带的分界线

sns.heatmap(data=data,cmap="RdBu_r",center=300)

 

细心的朋友可以察觉到颜色带上色彩两级的分界线变成了300

annot:是否显示数值注释

sns.heatmap(data=data,annot=True,cmap="RdBu_r")

 

 

怎么回事?乱码了吗?其实数值注释默认显示的是科学记数法的数值,我们得把数值进行格式化,这就用到了下面的参数。

fmt:format的缩写,设置数值的格式化形式

sns.heatmap(data=data,annot=True,fmt="d",cmap="RdBu_r")
#foramt为int类型

linewidths:控制每个小方格之间的间距

sns.heatmap(data=data,annot=True,fmt="d",linewidths=0.3,cmap="RdBu_r") 
#可以看到每个小方格之产生了间隙

 

linecolor:控制分割线的颜色

sns.heatmap(data=data,annot=True,fmt="d",linewidths=0.3,linecolor="grey",cmap="RdBu_r")
#原来的白色间隙变成了灰色间隙

 

cbar_kws:关于颜色带的设置

sns.heatmap(data=data,annot=True,fmt="d",cmap="RdBu_r",
                      cbar_kws={"orientation":"horizontal"}) 
#横向显示颜色帮

 

ask:传入布尔型矩阵,若为矩阵内为True,则热力图相应的位置的数据将会被屏蔽掉(常用在绘制相关系数矩阵图)

import numpy as np 
#随机生成一个200行10列的数据集 
data_new = np.random.randn(200,10) 
#求出这个数据集的相关系数矩阵 corr = np.corrcoef(data_new,rowvar=False) 
#以corr的形状生成一个全为0的矩阵 
mask = np.zeros_like(corr)
#将mask的对角线及以上设置为True
#这部分就是对应要被遮掉的部分mask[np.triu_indices_from(mask)] = True
with sns.axes_style("white"):
    sns.heatmap(corr, mask=mask, vmax=0.3, annot=True,cmap="RdBu_r")

 

如果大家对上面的代码流程不大了解,可以把mask打印出来看看

 

参照mask和上面绘制的图,应该就很容易理解了,mask中为1的部分,就是要被盖掉的部分。演示到此为止,想更深入的学习可以自行查阅官方文档! 

``` import numpy as np import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout import matplotlib.pyplot as plt import cv2 tf.random.set_seed(42) # 定义路径 train_dir = r'C:\Users\29930\Desktop\结构参数图' validation_dir = r'C:\Users\29930\Desktop\测试集路径' # 需要实际路径 # 图像参数 img_width, img_height = 150, 150 batch_size = 32 # 数据生成器 train_datagen = ImageDataGenerator( rescale=1./255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) test_datagen = ImageDataGenerator(rescale=1./255) train_generator = train_datagen.flow_from_directory( train_dir, # 修改为实际路径 target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary') validation_generator = test_datagen.flow_from_directory( validation_dir, # 修改为实际路径 target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary') # 模型构建 model = Sequential([ Conv2D(32, (3,3), activation='relu', input_shape=(img_width, img_height, 3)), MaxPooling2D(2,2), Conv2D(64, (3,3), activation='relu'), MaxPooling2D(2,2), Flatten(), Dense(128, activation='relu'), Dropout(0.5), Dense(1, activation='sigmoid') ]) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # 训练模型 history = model.fit( train_generator, steps_per_epoch=len(train_generator), epochs=20, validation_data=validation_generator, validation_steps=len(validation_generator)) # Grad-CAM函数修正版 def generate_grad_cam(model, img_array, layer_name): # 创建梯度模型 grad_model = tf.keras.models.Model( inputs=[model.inputs], outputs=[model.get_layer(layer_name).output, model.output] ) # 计算梯度 with tf.GradientTape() as tape: conv_outputs, predictions = grad_model(img_array) loss = predictions[:, 0] # 获取梯度 grads = tape.gradient(loss, conv_outputs) pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2)) # 生成力图 conv_outputs = conv_outputs[0] heatmap = tf.reduce_sum(conv_outputs * pooled_grads, axis=-1) # 归一化处理 heatmap = np.maximum(heatmap, 0) heatmap /= np.max(heatmap) return heatmap # 可视化部分 X, y = next(validation_generator) sample_image = X[0] img_array = np.expand_dims(sample_image, axis=0) # 获取最后一个卷积层(根据模型结构调整索引) last_conv_layer = model.layers[2] # 第二个Conv2D层 heatmap = generate_grad_cam(model, img_array, last_conv_layer.name) # 调整力图尺寸 heatmap = cv2.resize(heatmap, (img_width, img_height)) heatmap = np.uint8(255 * heatmap) # 颜色映射 jet = plt.colormaps.get_cmap('jet') jet_colors = jet(np.arange(256))[:, :3] jet_heatmap = jet_colors[heatmap] # 叠加显示 superimposed_img = jet_heatmap * 0.4 + sample_image superimposed_img = np.clip(superimposed_img, 0, 1) # 绘制结果 plt.figure(figsize=(12, 4)) plt.subplot(131) plt.imshow(sample_image) plt.title('原始图像') plt.axis('off') plt.subplot(132) plt.imshow(heatmap, cmap='jet') plt.title('力图') plt.axis('off') plt.subplot(133) plt.imshow(superimposed_img) plt.title('叠加效果') plt.axis('off') plt.tight_layout() plt.show()```Traceback (most recent call last): File "D:/建模/retu2.py", line 98, in <module> heatmap = generate_grad_cam(model, img_array, last_conv_layer.name) File "D:/建模/retu2.py", line 75, in generate_grad_cam outputs=[model.get_layer(layer_name).output, model.output] raise AttributeError( AttributeError: The layer sequential has never been called and thus has no defined output.. Did you mean: 'outputs'?
03-24
<think>嗯,用户提供的代码在执行时出现了AttributeError,具体是在调用generate_grad_cam函数时,模型层的输出无法获取。错误信息提到“The layer sequential has never been called and thus has no defined output.”,这说明在构建梯度模型时,可能没有正确指定层的输出。 首先,我需要理解Grad-CAM的实现原理。Grad-CAM需要获取卷积层的输出和模型的预测结果,然后通过梯度计算得到力图。用户代码中使用的是Sequential模型,可能层的名称或索引引用有误。在模型构建部分,用户定义的模型结构是: model = Sequential([ Conv2D(32, (3,3), activation='relu', input_shape=(img_width, img_height, 3)), MaxPooling2D(2,2), Conv2D(64, (3,3), activation='relu'), MaxPooling2D(2,2), Flatten(), Dense(128, activation='relu'), Dropout(0.5), Dense(1, activation='sigmoid') ]) 这里的第二个Conv2D层是索引为2的层(因为索引从0开始)。在代码中,用户尝试通过model.layers[2]来获取最后一个卷积层,但实际上最后一个卷积层应该是第三层?不,仔细看一下:第一层是Conv2D(32),索引0;第二层是MaxPooling2D,索引1;第三层是Conv2D(64),索引2;第四层是MaxPooling2D,索引3。所以,最后一个卷积层确实是索引2的层,对吗?是的。所以这里没问题。 但错误发生在创建梯度模型的时候,使用model.get_layer(layer_name).output时,可能该层没有被正确调用过,导致输出未定义。在TensorFlow中,如果在构建模型后没有进行实际的预测或调用,某些层可能没有被构建,因此输出张量可能未初始化。不过,通常在model.fit之后,模型已经被构建,所以这个问题可能不是由层未被调用引起的。 另一个可能性是层的名称问题。当使用Sequential模型时,层的名称会自动生成,例如“conv2d_1”等。用户可能在获取层名时使用了错误的名称。例如,在代码中,用户用last_conv_layer = model.layers[2],然后传递last_conv_layer.name给generate_grad_cam函数。如果这个名称在模型中被正确引用,应该没问题。但是错误信息中提到的是“The layer sequential”,似乎是指整个模型被当作层来获取,这可能意味着layer_name参数传递的是错误的名称,比如传入了模型的名称而不是层的名称。 查看错误信息中的具体行数,用户代码的第75行是创建grad_model的部分: grad_model = tf.keras.models.Model( inputs=[model.inputs], outputs=[model.get_layer(layer_name).output, model.output] ) 如果这里的layer_name是模型的名称(比如"sequential"),而不是某个层的名称,就会出现这个错误。例如,如果last_conv_layer.name返回的是“sequential”或者类似的模型级名称,而不是具体的层名称,那么model.get_layer(layer_name)就会试图获取模型作为层,导致错误。 因此,需要检查last_conv_layer.name的值。用户模型中各层的名称可以通过model.summary()查看,或者在代码中打印出来。例如,第二个Conv2D层的名称可能是“conv2d_1”,因为第一个Conv2D是“conv2d”,而第二个可能会自动命名为“conv2d_1”。或者,如果用户使用的是不同的TensorFlow版本,命名方式可能有所不同。 用户可能在代码中错误地将layer_name设置为模型的名字,比如model.name,而不是具体层的名称。比如,如果模型本身被命名为“sequential”,而用户错误地将layer_name参数设置为模型的名称,那么就会出现这个问题。但在用户的代码中,last_conv_layer是从model.layers[2]获取的,也就是第三个层(第二个Conv2D层),所以它的名称应该是类似“conv2d_1”的名称,而非“sequential”。 不过,在错误信息中,报错提到的是“The layer sequential has never been called...”,这可能意味着在model.get_layer(layer_name)中,传入的layer_name是“sequential”,即模型本身的名称,而不是层的名称。这说明在代码中,last_conv_layer.name的值实际上是“sequential”,这显然不正确。那为什么会这样呢? 可能用户错误地将last_conv_layer赋值为模型本身,而不是某个层。例如,错误地使用model.layers[-1]或者索引错误。例如,在用户代码中: last_conv_layer = model.layers[2] # 第二个Conv2D层 但如果模型的结构中,model.layers[2]实际上不是卷积层,而是其他类型的层,比如MaxPooling2D?或者,可能用户在构建模型时层的顺序有误? 再仔细看用户模型的结构: 索引0: Conv2D(32) 索引1: MaxPooling2D 索引2: Conv2D(64) 索引3: MaxPooling2D 索引4: Flatten 索引5: Dense 索引6: Dropout 索引7: Dense 因此,model.layers[2]确实是第二个Conv2D层,其名称应为“conv2d_1”(假设第一个Conv2D是“conv2d”)。此时,last_conv_layer.name应该是正确的,如“conv2d_1”。 那为什么会出现AttributeError,指出层名为“sequential”呢? 这可能是因为用户使用的TensorFlow版本中,模型的层命名方式不同,或者在模型构建时存在其他问题。例如,在较早的版本中,可能层的名称没有被正确设置。 另一种可能性是,用户代码中的model尚未构建,导致层的输出未被定义。例如,如果在构建模型后没有进行训练或预测,某些层可能没有创建权重,从而无法获取输出。但用户代码中已经调用了model.fit(),所以模型应该已经被构建,各层的输出应该已经定义。 那问题可能出在generate_grad_cam函数中的grad_model定义。在创建grad_model时,是否正确引用了层的输出? 或者,可能是TensorFlow的版本问题。例如,在旧版本中,model.inputs可能返回的是单个输入,而新版本可能需要明确指定输入。或者,model.get_layer(layer_name)在某种情况下无法正确获取层。 或者,用户可能在定义grad_model时,输入和输出的设置不正确。例如,是否应该使用model.input而不是model.inputs?或者需要以列表形式传递输入? 例如,原代码中的: grad_model = tf.keras.models.Model( inputs=[model.inputs], outputs=[model.get_layer(layer_name).output, model.output] ) 这里model.inputs返回的是模型的输入张量列表,而作为Model的inputs参数,应该传递的是输入层或输入张量。正确的写法可能是: inputs = model.inputs outputs = [model.get_layer(layer_name).output, model.output] grad_model = tf.keras.models.Model(inputs=inputs, outputs=outputs) 或者,在旧版本中,可能需要使用model.input而不是model.inputs,但model.inputs应该是一个列表,包含所有输入张量。对于单输入模型,model.inputs[0]就是输入张量。 但不管怎样,上述代码是否正确?例如,假设model.inputs是一个列表,那么inputs=[model.inputs]可能变成列表的列表,导致错误。正确的写法应该是inputs=model.inputs,而不是inputs=[model.inputs]。例如: grad_model = tf.keras.models.Model( inputs=model.inputs, outputs=[model.get_layer(layer_name).output, model.output] ) 如果原来的代码中inputs被写成了[model.inputs],那么输入就变成了包含一个列表的列表,这可能导致错误。例如,model.inputs本身已经是输入张量的列表(虽然这里模型是单输入),所以正确的应该是inputs=model.inputs。例如: 原代码中的inputs=[model.inputs] → 改为 inputs=model.inputs 或者,如果模型只有一个输入,可以写成inputs=model.input,即: grad_model = tf.keras.models.Model( inputs=model.input, outputs=[model.get_layer(layer_name).output, model.output] ) 这可能更准确,因为model.input是单个输入张量,而model.inputs是包含该张量的列表。所以在创建新的模型时,传入inputs=model.input会更合适,而不是一个列表。 如果用户的代码中将inputs设置为[model.inputs],即一个列表的列表,这可能导致模型构建时无法正确识别输入,从而在获取层输出时出现问题,导致错误。例如,在TensorFlow中,如果传入的inputs结构不正确,可能会导致内部层的输出未被正确构建,从而引发属性错误。 因此,解决这个问题的方法是修改grad_model的创建方式,将inputs参数改为model.inputs或model.input,而不是[model.inputs]。 例如,原代码中的: inputs=[model.inputs] → 改为 inputs=model.inputs 或者: inputs=model.input 假设model是单输入模型,那么两种方式都可以,但[model.inputs]会导致输入变成一个包含列表的元素,这可能引起问题。 假设用户的代码中存在这个错误,导致grad_model的输入不正确,从而无法正确获取层的输出,进而出现AttributeError。 因此,修改生成grad_model的代码部分: 将: grad_model = tf.keras.models.Model( inputs=[model.inputs], outputs=[model.get_layer(layer_name).output, model.output] ) 改为: grad_model = tf.keras.models.Model( inputs=model.inputs, outputs=[model.get_layer(layer_name).output, model.output] ) 或者更准确地: grad_model = tf.keras.models.Model( inputs=model.input, outputs=[model.get_layer(layer_name).output, model.output] ) 这可能解决问题。 另一个可能性是,在生成grad_model时,需要确保所有涉及的层都已经被调用。例如,如果在定义模型时某些层未被正确连接,可能出现问题。但用户已经训练了模型,因此各层应该已经被正确构建。 总结可能的问题和解决方案: 1. 在创建grad_model时,inputs参数错误地使用了[model.inputs],应改为model.inputs或model.input。 2. 获取最后一个卷积层的索引错误,导致layer_name不正确。例如,用户可能错误地选择了非卷积层。 3. TensorFlow版本差异导致层的输出无法正确获取,可能需要其他方式构建grad_model。 用户遇到的错误信息中提到的“The layer sequential”说明在尝试获取名为“sequential”的层,而实际上该名称属于模型本身,而非某个层。这意味着在调用model.get_layer(layer_name)时,layer_name的值是“sequential”,即模型的名字,而不是某个层的名字。这显然不正确,因此需要检查layer_name的传递是否正确。 在用户的代码中,layer_name是通过last_conv_layer.name获得的,而last_conv_layer是model.layers[2]。因此,必须确认model.layers[2]的name属性是否正确。例如,在模型中,第二个Conv2D层的名称可能不是预期的,比如如果第一个Conv2D层的名称是“conv2d”,第二个可能是“conv2d_1”,或者其他情况。 用户可以在代码中添加print语句,输出last_conv_layer.name,以确认其值是否正确。例如: print("Last conv layer name:", last_conv_layer.name) 如果输出的名称是“sequential”,则说明此处存在错误,但为什么会这样呢?因为model.layers[2]应该是第二个Conv2D层,其名称不应该与模型相同。可能用户在定义模型时,错误地将模型命名为“sequential”,而层的命名存在冲突?或者模型本身的名称为“sequential”,而用户错误地传入了模型名称? 或者,用户可能在代码中错误地将last_conv_layer赋值为模型本身,例如: last_conv_layer = model # 错误,这里应该是model.layers[2] 但在用户代码中,这一行是正确的: last_conv_layer = model.layers[2] # 第二个Conv2D层 所以问题可能出在TensorFlow层的命名机制。例如,在Sequential模型中,层的名称可能被自动命名为“sequential”的一部分?这似乎不太可能。 另一种可能性是,用户在定义模型时,没有正确命名各层,导致model.layers[2]的名称被错误地设置为“sequential”。例如,用户可能在创建Conv2D层时,传入了name参数,错误地设置为“sequential”,但这不太可能,因为用户代码中没有显式设置层的名称。 综上,可能的问题在于创建grad_model时,inputs参数被错误地设置为[model.inputs],导致模型构建错误,进而无法正确获取层的输出。或者,layer_name的值被错误地设置为模型的名称而非层的名称。 因此,解决方案包括: 1. 修正grad_model的输入定义,将inputs=[model.inputs]改为inputs=model.inputs或inputs=model.input。 2. 确保传递给generate_grad_cam函数的layer_name参数是正确的卷积层名称,例如“conv2d_1”。 另外,用户还可以在代码中添加打印语句,确认最后一个卷积层的名称是否正确。例如: print("Last conv layer name:", last_conv_layer.name) 如果输出的名称是正确的卷积层名称,如“conv2d_1”,则问题可能出在inputs参数的设置上。 假设用户修正了inputs参数,但仍然出现错误,则可能需要进一步检查其他部分。 例如,在计算梯度时,是否正确处理了多个输出的情况?在grad_model中,outputs是两个张量:卷积层的输出和模型的输出。在tape.watch的时候,是否所有必要的变量都被正确追踪? 或者,在计算梯度时,loss的计算是否正确。例如,用户代码中的loss = predictions[:, 0],当模型输出是二分类的sigmoid结果时,这可能正确,因为输出形状是(batch_size, 1),所以predictions[:,0]是正确的索引。 总结,最可能的解决方案是修正grad_model的输入设置,并确保layer_name是正确的卷积层名称。用户需要检查这两部分,并进行相应的修改。</think>该错误是由于Grad-CAM实现中模型构建方式不当导致的。以下是分步解决方案: **步骤1:修正梯度模型构建方式** 原代码中的输入参数格式有误,应修改为: ```python grad_model = tf.keras.models.Model( inputs=model.inputs, # 修改这里 outputs=[model.get_layer(layer_name).output, model.output] ) ``` **步骤2:确认卷积层索引** 通过打印层信息验证卷积层选择: ```python print("模型层结构:") for idx, layer in enumerate(model.layers): print(f"层{idx}: {layer.name} -> {layer.__class__.__name__}") # 选择最后一个卷积层(索引2对应第二个Conv2D) last_conv_layer = model.layers[2] print("选中的卷积层名称:", last_conv_layer.name) ``` **完整修正代码**(关键部分): ```python # Grad-CAM函数修正版 def generate_grad_cam(model, img_array, layer_name): # 创建梯度模型(修正输入参数格式) grad_model = tf.keras.models.Model( inputs=model.inputs, # 关键修正点 outputs=[model.get_layer(layer_name).output, model.output] ) # 其余代码保持不变... # 验证层选择 print("模型层结构验证:") for idx, layer in enumerate(model.layers): print(f"层{idx}: {layer.name} ({layer.__class__.__name__})") # 应选择第二个Conv2D层(索引2) last_conv_layer = model.layers[2] ``` **技术原理说明**: 1. **Grad-CAM机制**:通过计算目标类别的梯度对最后一个卷积层特征图的加权平均,定位重要区域 2. **层输出获取**:必须使用正确格式获取中间层输出,`model.inputs`直接传递输入张量列表 3. **卷积层选择**:典型CNN结构中,越深的卷积层包含越高级的特征,通常选择最后一个卷积层 **扩展建议**: 1. 添加力图强度调节参数: ```python def generate_grad_cam(..., alpha=0.4): # 叠加时使用可调参数 superimposed_img = jet_heatmap * alpha + sample_image ``` 2. 添加异常处理: ```python try: layer_output = model.get_layer(layer_name).output except Exception as e: print(f"层获取失败: {e},可用层列表:") print([layer.name for layer in model.layers]) raise ``` **验证方法**: ```python # 训练后立即执行模型结构验证 model.summary() print("\n最后一个卷积层详细信息:") print(last_conv_layer.get_config()) ``` 通过以上修正,可解决层输出未定义的错误,确保Grad-CAM正确生成可视化力图
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值