栈之心得

本文深入探讨了栈这种特殊线性表的结构与操作,包括入栈、出栈、判断空栈等基本功能,以及顺序栈和链栈的实现方式。通过对比两种栈的优缺点,介绍了栈在中缀表达式求值、转换为后缀表达式以及后缀表达式求值中的应用。

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

栈—是一种特殊的线性表

栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

栈的特殊之处在于:仅在表尾进行插入和删除操作的线性表。也就是先进后出,只有后入的数据出栈,之前的数据才能出栈。
空栈:不含任何数据元素的栈。
允许插入和删除的一端称为栈顶,另一端称为栈底。

在这里插入图片描述
需要搞清楚的是:栈只是对表插入和删除操作的位置进行了限制,并没有限定插入和删除操作进行的时间。
对于栈的实现:首先是自己定义的栈:
1.顺序栈—栈的顺序存储结构
不同的函数:
①入栈:

void  Push( T  x )
template <class T>
void  seqStack<T>::Push ( T  x)
{
     if (top==MAX_SIZE-1)  throw  “溢出”;
     top++;
     data[top]=x;
 } 

②判断是否是空栈:

bool Empty( )
template <class T>
bool  seqStack<T>::Empty ()
{
     if (top==-1)  
         return true;
     return false;
 } 

③取栈顶

T  GetTop(  )
template <class T>
T  seqStack<T>::GetTop ( )
{
     if (Empty()) throw  ”空栈” ;
     return data[top];
 } 

④出栈
T Pop( )

template  <class  T>
T  seqStack<T>:: Pop ( )
{
     if (top==-1)  throw  “溢出”; 
     x=data[top];
      top--;
     return  x;
 }

顺序栈还可以一个数组来储存两个栈,两栈共享空间:使用一个数组来存储两个栈,让一个栈的栈底为该数组的始端,另一个栈的栈底为该数组的末端,两个栈从各自的端点向中间延伸。
栈1的底固定在下标为0的一端;栈2的底固定在下标为StackSize-1的一端。top1和top2分别为栈1和栈2的栈顶指针;
Stack_Size为整个数组空间的大小;

在这里插入图片描述
当top1= -1的时候栈1为空,当top2= Stack_Size栈2为空,当top1+1=top2时栈满。

2.栈之链式存储
通常将将链头作为栈顶,方便操作,利用头插法,并在再进行出栈的时候,秩序便利一个数据。且链栈不需要附设头结点。
在这里插入图片描述
①入栈:

void Push(T x)
template <class T>
void LinkStack<T>::Push(T x)
{
    s=new Node<T>;
    s->data=x; 
    s->next=top; 
   top=s; 
}

②出栈:

T Pop( )
template <class T>
T LinkStack<T>::Pop( )
{
    if (top==NULL)
     throw "下溢";
    x=top->data; 
    p=top; 
    top=top->next;   
    delete p;
    return x;
}

③析构

template <class T>
LinkStack<T>::~LinkStack( )
{
	while (top)
	{
		Node<T> *p;
		p=top->next;
        		delete top;
		top=p;
	}
}

顺序栈和链栈的比较
时间性能:相同,都是常数时间O(1)
空间性能:
顺序栈:有元素个数的限制和空间浪费的问题。
链栈:没有栈满的问题,只有当内存没有可用空间时才会出现栈满,但是每个元素都需要一个指针域,从而产生了结构性开销。 结论:当栈的使用过程中元素个数变化较大时,用链栈是适宜的,反之,应该采用顺序栈。

对于栈的一些应用
中缀表达式求值
中缀表达式转化为后缀表达式
后缀表达式求值
后缀表达式求值——
从左到右对后缀表达式字符串进行处理,每次处理一个符号
若遇到数字,入栈
若遇到运算符,栈顶两个数字出栈,执行运算符所定义的运算,并将运算结果入栈
重复以上的工作,直到表达式结束,此时,栈中的数字代表最终的值。

中缀表达式求值过程
设置两个栈:
OVS(运算数栈)和OPTR(运算符栈)。
自左向右扫描中缀表达式,
遇操作数进OVS,
遇操作符则与OPTR栈顶优先数比较:
OPTR栈顶<当前操作符, 当前操作符进OPTR栈
OPTR栈顶>=当前操作符,OVS栈顶、次顶和OPTR栈顶,退栈形成运算T(i),T(i)进OVS栈。

中缀表达式转化为后缀表达式——
设置一个运算符栈。从左到右依次对中缀表达式中的每个符号进行处理
如果遇到数字,直接输出
如果遇到“(”,则将其入栈
如果遇到运算符a,如果栈顶符号的优先级低于a的优先级,则入栈;否则,栈顶符号出栈,直到栈顶符号的优先级低于a的优先级,此时让a入栈
若遇到“)”,则栈顶符号出栈,直到“(”
重复以上工作,直到表达式结束,此时,将栈里符号全部出栈。

二.STL的栈
对于自己写的栈再进行操作时难免会出现或遇到一些问题,所以运用C++ Stack(堆栈)它 是一个容器类的改编,为程序员提供了堆栈的全部功能,——也就是说实现了一个先进后出(FILO)的数据结构。
c++ STL栈stack的头文件为: #include
c++ STL栈stack的成员函数介绍
操作 比较和分配堆栈
empty() 堆栈为空则返回真
pop() 移除栈顶元素
push() 在栈顶增加元素
top() 返回栈顶元素
在学习栈后对线性表的理解更加深刻和透彻,再次回顾线性表的问题是也没有之前写代码时的迷茫,对于顺序结构和链式结构都有了更深刻的了解,栈作为一种特殊的线性表,可以解决许多的问题,有括号匹配,火车等问题。

<think>嗯,用户提到的YOLO训练结果都是0nan,这个问题确实比较棘手。首先,我需要回忆一下常见的训练失败原因。通常,出现这种情况可能有几个方面的问题:数据、模型配置、超参数设置训练环境的问题。 首先,检查数据是否正确。用户的数据集是否有标注错误?比如,标签文件中的类别是否正确,边界框坐标是否在合理范围内(0到1之间)。如果标签有问题模型可能无法学习到有效的特征,导致损失值不下降者出现NaN。 接下来,数据加载部分。可能在读取数据时出现了错误,比如图像路径不正确,者数据预处理步骤有误。例如,如果输入图像的尺寸不符合模型要求,者归一化处理不当,都可能导致模型无法收敛。 然后是模型配置。YOLO的不同版本有不同的配置文件,用户是否正确选择了对应的模型结构?比如,类别数是否设置正确。如果类别数不对,最后的分类层输出可能与标签不匹配,导致损失计算错误,出现NaN。 损失函数部分也需要检查。YOLO的损失包括box_loss、cls_loss和dfl_loss。如果某个损失项出现NaN,可能是由于计算过程中出现了除以零者对数负数的情况。比如,分类损失为NaN,可能是因为模型的输出概率出现了极端值,需要检查是否使用了正确的激活函数(如SigmoidSoftmax),以及是否有梯度爆炸的情况。 学习率和优化器设置也是一个常见问题。如果学习率过高,可能会导致梯度更新过大,权重变得不稳定,损失值剧烈波动甚至变为NaN。这时候可以尝试降低学习率,者使用学习率预热策略。 另外,检查训练日志中的警告信息。用户提到有TensorBoard的警告,关于图可视化失败。这可能是因为模型在导出计算图时遇到了不支持的数据结构,但这个问题可能不影响训练本身,但需要确认是否有其他更严重的错误信息被忽略。 还有可能的是硬件问题,比如GPU内存不足导致的数据处理错误,但用户显示GPU内存使用量在4G左右,应该不是内存不足的问题。不过,可以检查是否有显存溢出的情况,者混合精度训练的设置是否正确。 最后,建议用户逐步排查:首先验证数据集的正确性,然后检查模型配置和超参数,接着尝试使用更小的学习率,者在一个很小的子集上进行过拟合测试,看看模型是否能够正常收敛。如果在小数据集上仍然出现问题,那么问题可能出在模型结构数据预处理上;如果能够过拟合,那么可能需要调整数据增强正则化策略。</think>在YOLO模型训练过程中出现损失值全为0NaN以及mAP指标全为0的情况,通常是由以下原因导致的。请按照以下步骤逐步排查: --- ### 一、核心问题分析 从输出日志可见: - `box_loss=0`:目标检测未学习到有效特征 - `cls_loss=nan`:分类预测出现数值不稳定 - `mAP全为0`:模型完全失效 - `dfl_loss=0`:分布焦点损失未生效 --- ### 二、常见原因及解决方案 #### 1. 数据问题(首要排查) - **标签格式错误** - 检查标注文件(YOLO格式要求): ```text <class_id> <x_center> <y_center> <width> <height> ``` - 坐标必须归一化到[0,1] - 类别ID必须从0开始连续编号 - 使用官方验证工具: ```bash from ultralytics.yolo.utils.checks import check_det_dataset check_det_dataset('your_dataset.yaml') ``` #### 2. 配置文件错误 - **类别数不匹配** - 检查`*.yaml`配置文件中`nc`参数是否与实际类别数一致 - 示例配置片段: ```yaml # yolov8.yaml nc: 10 # 必须与数据集实际类别数一致 ``` #### 3. 学习率问题(常见NaN原因) - **学习率过大导致梯度爆炸** - 尝试降低初始学习率(推荐从`1e-4`开始) - 添加梯度裁剪: ```python optimizer = torch.optim.SGD(model.parameters(), lr=0.0001, momentum=0.9) torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=10) ``` #### 4. 损失计算问题 - **NaN具体分析**: ```math \text{cls\_loss} = -\sum (y \cdot \log(p) + (1-y) \cdot \log(1-p)) ``` - 出现`log(0)`会导致NaN,需检查: - 模型输出是否经过Sigmoid激活 - 标签中是否存在全0全1的情况 --- ### 三、调试建议 1. **最小化验证**: ```python # 使用单张图片测试过拟合能力 results = model.train(data="coco128.yaml", epochs=300, imgsz=640, batch=1, optimizer="Adam", lr0=0.0001, device=0) ``` - 如果仍无法收敛,说明存在基础配置错误 2. **可视化验证**: ```bash from ultralytics.yolo.utils.plotting import plot_images plot_images(imgs, labels, paths=save_dir/'labels.jpg') ``` 3. **梯度监控**: ```python # 在训练循环中添加 print(f"Max gradient: {max(p.grad.max() for p in model.parameters())}") ``` --- ### 四、典型错误排查表 | 现象 | 优先检查方向 | 工具方法 | |-------------|--------------------|---------------------------| | box_loss=0 | 标注文件 | labelImg可视化检查 | | cls_loss=nan | 激活函数 | 输出直方图可视化 | | mAP=0 | 数据加载 | `check_det_dataset()`函数 | | dfl_loss=0 | 损失权重配置 | hyp*.yaml文件检查 | --- 建议从数据质量开始逐步排查,使用官方验证工具检查数据集完整性。如果问题仍然存在,可提供完整的训练配置文件和数据集样本进行进一步分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值