分析torch tensorflow和numpy中的stack和concat

博客对比了torch、tensorflow和numpy三个库的拼接与堆叠方法。torch有cat和stack,tensorflow有concat和stack,numpy有concatenate和stack。虽语法命名不同,但逻辑相同,concat在现有轴拼接,stack先扩维再拼接,可视为concat附加功能。

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

一、torch

torch.cat and torch.stack

# 创建三个一维的tensor
t1 = torch.tensor([1, 1, 1])
t2 = torch.tensor([2, 2, 2]) 
t3 = torch.tensor([3, 3, 3])
# torch.cat在一维拼接
torch.cat((t1,t2,t3), dim=0)
输出: tensor([1, 1, 1, 2, 2, 2, 3, 3, 3])
# torch.stack在一维拼接
torch.stack((t1,t2,t3), dim=0)
输出: tensor([[1, 1, 1],
        [2, 2, 2],
        [3, 3, 3]])
# 对一维张量扩展dim=0即第一个轴的维度     
print(t1.unsqueeze(0).shape)
print(t1.unsqueeze(0))
输出: torch.Size([1, 3])
	  tensor([[1, 1, 1]])
torch.cat(
    (
        t1.unsqueeze(0), # 对t1张量扩展dim=0即第一个轴的维度
        t2.unsqueeze(0),
        t3.unsqueeze(0)
    ),
    dim=0
)
输出: tensor([[1, 1, 1],
        [2, 2, 2],
        [3, 3, 3]])
# torch.stack在第二维拼接
torch.stack((t1,t2,t3), dim=1)
输出: tensor([[1, 2, 3],
        [1, 2, 3],
        [1, 2, 3]])
# 对一维张量扩展dim=1即第二个轴的维度
print(t1.unsqueeze(1).shape)
print(t1.unsqueeze(1))
输出: torch.Size([3, 1])
	 tensor([[1],
	         [1],
	         [1]])
# torch.cat在第二维拼接
torch.cat(
    (
        t1.unsqueeze(1), # 对t1张量扩展dim=1即第二个轴的维度
        t2.unsqueeze(1),
        t3.unsqueeze(1)
    ),
    dim=1
)
输出: tensor([[1, 2, 3],
        [1, 2, 3],
        [1, 2, 3]])

二、tensorflow

tf.concat and tf.stack

# 创建三个一维的tensor
t1 = tf.constant([1, 1, 1])
t2 = tf.constant([2, 2, 2]) 
t3 = tf.constant([3, 3, 3])
# tf.concat在一维拼接
tf.concat((t1,t2,t3), axis=0)
输出: <tf.Tensor: shape=(9,), dtype=int32, numpy=array([1, 1, 1, 2, 2, 2, 3, 3, 3])>
# tf.stack在一维拼接
tf.stack((t1,t2,t3), axis=0)
输出: <tf.Tensor: shape=(3, 3), dtype=int32, numpy=
	array([[1, 1, 1],
	       [2, 2, 2],
	       [3, 3, 3]])>
# 对一维张量扩展axis=0即第一个轴的维度
print(tf.expand_dims(t1, 0).shape)
print(tf.expand_dims(t1, 0))
输出: (1, 3)
	 tf.Tensor([[1 1 1]], shape=(1, 3), dtype=int32)
tf.concat(
    (
        tf.expand_dims(t1, 0), # 对t1张量扩展axis=0即第一个轴的维度
        tf.expand_dims(t2, 0),
        tf.expand_dims(t3, 0)
    ),
    axis=0
)
输出: <tf.Tensor: shape=(3, 3), dtype=int32, numpy=
	array([[1, 1, 1],
	       [2, 2, 2],
	       [3, 3, 3]])>
# tf.stack在第二维拼接
tf.stack((t1,t2,t3), axis=1)
输出: <tf.Tensor: shape=(3, 3), dtype=int32, numpy=
	array([[1, 2, 3],
	       [1, 2, 3],
	       [1, 2, 3]])>
# 对一维张量扩展axis=1即第二个轴的维度
print(tf.expand_dims(t1, 1).shape)
print(tf.expand_dims(t1, 1))
输出: (3, 1)
	tf.Tensor(
	[[1]
	 [1]
	 [1]], shape=(3, 1), dtype=int32)
# tf.concat在第二维拼接
tf.concat(
    (
        tf.expand_dims(t1, 1), # 对t1张量扩展axis=1即第二个轴的维度
        tf.expand_dims(t2, 1),
        tf.expand_dims(t3, 1)
    ),
    axis=1
)
输出: <tf.Tensor: shape=(3, 3), dtype=int32, numpy=
	array([[1, 2, 3],
	       [1, 2, 3],
	       [1, 2, 3]])>

三、numpy

np.concatenate and np.stack

# 创建三个一维的array
t1 = np.array([1, 1, 1])
t2 = np.array([2, 2, 2])
t3 = np.array([3, 3, 3])
# np.concatenate在一维拼接
np.concatenate((t1,t2,t3), axis=0)
输出: array([1, 1, 1, 2, 2, 2, 3, 3, 3])
# np.stack在一维拼接
np.stack((t1,t2,t3), axis=0)
输出: array([[1, 1, 1],
	        [2, 2, 2],
	        [3, 3, 3]])
np.concatenate(
    (
        np.expand_dims(t1, 0), # 对t1数组扩展axis=0即第一个轴的维度
        np.expand_dims(t2, 0),
        np.expand_dims(t3, 0)
    ),
    axis=0
)
输出: array([[1, 1, 1],
            [2, 2, 2],
            [3, 3, 3]])
# np.stack在二维拼接
np.stack((t1,t2,t3),axis=1)
输出: array([[1, 2, 3],
            [1, 2, 3],
            [1, 2, 3]])
np.concatenate(
    (
        np.expand_dims(t1, 1), # 对t1数组扩展axis=1即第二个轴的维度
        np.expand_dims(t2, 1),
        np.expand_dims(t3, 1)
    ),
    axis=1
)
输出: array([[1, 2, 3],
	        [1, 2, 3],
	        [1, 2, 3]])

四、结论

  1. torch、tensorflow、numpy不同库的语法,命名可能不同但是实现的逻辑相同
  2. concat方法是在现有轴上拼接
  3. stack方法是在新轴上堆叠,即先扩维创建新轴,再内部调用concat拼接,可以看做是concat的附加功能
  4. 下面是numpy的concatenate源码

在这里插入图片描述

### YOLOv5 中 `train.py` 出现 `AttributeError: 'list' object has no attribute 'shape'` 的原因分析 该错误通常发生在模型定义或数据处理阶段,当尝试调用列表对象的 `.shape` 属性时会引发此异常。这是因为 Python 列表本身并不具备 `.shape` 属性,而这是 NumPy 数组或 PyTorch 张量才有的属性。 #### 错误的根本原因 在代码中可能存在如下情况: - 将本应为张量的对象错误地赋值为了列表。 - 数据预处理过程中返回的结果被封装成了列表而非张量。 - 模型输出层未正确配置,导致其返回的是列表而不是预期的张量结构。 具体到YOLOv5中的实现,可能涉及以下几个方面的问题[^4]: 1. **模型定义部分**:如果模型的输入或输出层被错误设置成列表形式,则可能导致此类错误。 2. **数据加载器**:数据管道中某些操作可能会意外将张量转换为列表。 3. **损失函数计算**:类似于YOLOv9的情况,在拼接特征图或将多个分支结果组合时可能出现类似的错误[^2]。 #### 解决方案 以下是几种常见的修复方式: ##### 方法一:调整模型输出层类型 确认模型构建时是否将输出层指定为单个张量或者由多个子模块组成的序列容器(Sequential)。如果是后者,请确保最终输出仍然是一个整体张量而非嵌套列表。例如可以修改如下代码片段来修正潜在问题: ```python model = Model(inputs=input_layer, outputs=output_layers) ``` 改为显式声明outputs参数为单一Tensor实例: ```python if isinstance(output_layers, list): output_tensor = tf.concat(output_layers, axis=-1) # 或者其他适合的操作符 else: output_tensor = output_layers model = Model(inputs=[input_layer], outputs=[output_tensor]) ``` ##### 方法二:检查并修正数据增强逻辑 有时自定义的数据增广流程也可能引入不兼容的数据格式变化。建议逐一排查transformations步骤,并验证每一步骤后的batch样本确实保留了原始维度信息且仍保持为torch.Tensor类型。 ##### 方法三:更新依赖库版本 考虑到框架迭代带来的API变更影响,适当升级pytorch及相关工具包至最新稳定版亦有助于规避一些已知缺陷触发上述异常的可能性。 通过以上措施应该能够有效定位并消除所描述类型的attributeerror现象发生几率。 ```python import torch.nn as nn class ExampleModel(nn.Module): def __init__(self): super().__init__() self.layers = nn.Sequential( nn.Conv2d(in_channels=3,out_channels=6,kernel_size=(5,5)), ... ) def forward(self,xs:list)->torch.Tensor: """ Ensure input xs converted into proper tensor before processing """ if not all(isinstance(x,torch.Tensor)for x in xs): raise ValueError('Inputs must be tensors') combined_input=torch.stack(xs,dim=0)# Assuming batched inputs here. result=self.layers(combined_input) return result.flatten(start_dim=1,end_dim=-1) # Adjust according to actual needs. example_model=ExampleModel() dummy_data=[torch.rand((3,H,W))]*BATCH_SIZE try: predictions=example_model(dummy_data) except Exception as e: print(f"Caught exception during inference:{e}") finally: del example_model,dummy_data;gc.collect() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值