分享

yolo v2 损失函数源码解读

 Rainbow_Heaven 2017-10-17

前提说明:

    1, 关于 yolo 和 yolo v2 的详细解释请移步至如下两个链接,或者直接看论文(我自己有想写 yolo 的教程,但思前想后下面两个链接中的文章质量实在是太好了_(:з」∠)_)

        yolo: https://zhuanlan.zhihu.com/p/24916786?refer=xiaoleimlnote

        yolo v2: https://zhuanlan.zhihu.com/p/25167153

    2, 本文仅解读 yolo v2 的 loss 函数的源码,该代码请使用如下命令

            git clone https://github.com/pjreddie/darknet

        后打开 src/region_layer.c 查看

    3, yolo 的官方网站地址为:https:///darknet/yolo/

    4, 我调试代码时使用的命令是:

            ./darknet detector train cfg/voc.data cfg/yolo-voc.cfg darknet19_448.conv.23


代码解读:

  1. region_layer.c  
  2. box get_region_box(float *x, float *biases, int n, int index, int i, int j, int w, int h, int stride)  
  3. {  
  4.     box b;  
  5.     b.x = (i + x[index + 0*stride]) / w;  
  6.     b.y = (j + x[index + 1*stride]) / h;  
  7.     b.w = exp(x[index + 2*stride]) * biases[2*n]   / w;  
  8.     b.h = exp(x[index + 3*stride]) * biases[2*n+1] / h;  
  9.     //printf("%f/%d/%d - %f/%f/%f/%f\n", x[index + 2*stride], w, h, b.x, b.y, b.w, b.h);  
  10.     return b;  
  11. }  
  12.   
  13. float delta_region_box(box truth, float *x, float *biases, int n, int index, int i, int j, int w, int h, float *delta, float scale, int stride)  
  14. {  
  15.     box pred = get_region_box(x, biases, n, index, i, j, w, h, stride);  
  16.     float iou = box_iou(pred, truth);  
  17.   
  18.     float tx = (truth.x*w - i);  
  19.     float ty = (truth.y*h - j);  
  20.     float tw = log(truth.w*w / biases[2*n]);  
  21.     float th = log(truth.h*h / biases[2*n + 1]);  
  22.   
  23.     delta[index + 0*stride] = scale * (tx - x[index + 0*stride]);  
  24.     delta[index + 1*stride] = scale * (ty - x[index + 1*stride]);  
  25.     delta[index + 2*stride] = scale * (tw - x[index + 2*stride]);  
  26.     delta[index + 3*stride] = scale * (th - x[index + 3*stride]);  
  27.     return iou;  
  28. }  
  29.   
  30. void forward_region_layer()  
  31. {  
  32.     ...  
  33.       
  34.     for (b = 0; b < l.batch; ++b) {  
  35.         if(l.softmax_tree){  
  36.             // 没执行  
  37.         }  
  38.         // 下面的 for 循环是计算没有物体的 box 的 confidence 的 loss  
  39.         // 1, 遍历所有格子以及每个格子的 box,计算每个 box 与真实 box 的 best_iou  
  40.         // 2, 先不管三七二十一,把该 box 当成没有目标来算 confidence 的 loss   
  41.         // 3, 如果当前 box 的 best_iou > 阈值,则说明该 box 是有物体的,于是上面哪行计算的 loss 就不算数,因此把刚才计算的 confidence 的 loss 清零。  
  42.         // 假设图片被分成了 13 * 13 个格子,那 l.h 和 l.w 就为 13  
  43.         // 于是要遍历所有的格子,因此下面就要循环 13 * 13 次  
  44.         for (j = 0; j < l.h; ++j) {  
  45.             for (i = 0; i < l.w; ++i) {  
  46.                   // 每个格子会预测 5 个 boxes,因此这里要循环 5 次  
  47.                 for (n = 0; n < l.n; ++n) {  
  48.                       // 获得 box 的 index  
  49.                     int box_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, 0);  
  50.                       // 获得 box 的预测 x, y, w, h,注意都是相对值,不是真实坐标  
  51.                     box pred = get_region_box(l.output, l.biases, n, box_index, i, j, l.w, l.h, l.w*l.h);  
  52.                     float best_iou = 0;  
  53.                     // 下面的循环 30 次我是这么理解的:  
  54.                       //        假设一张图片中最多包含 30 个物体,于是对每一个物体求 iou  
  55.                       // PS:我看了很久都没找到这个 30 能和什么关联上,于是猜测 30 的含义是“假设一张图片中最多包含 30 个物体”。  
  56.                     for(t = 0; t < 30; ++t){  
  57.                         // get truth_box's x, y, w, h  
  58.                         box truth = float_to_box(net.truth + t*5 + b*l.truths, 1);  
  59.                         printf("\ti=%d, j=%d, n=%d, t=%d\n", i, j, n, t);  
  60.                         // 遍历完图片中的所有物体后退出  
  61.                         if(!truth.x){  
  62.                             break;  
  63.                         }  
  64.                         float iou = box_iou(pred, truth);  
  65.                         if (iou > best_iou) {  
  66.                             best_iou = iou;  
  67.                         }  
  68.                     }  
  69.                     // 获得预测结果中保存 confidence 的 index  
  70.                     int obj_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, 4);  
  71.                     avg_anyobj += l.output[obj_index];  
  72.                     // 这里先不管三七二十一,直接把该 box 当成没有目标来算 loss 了。  
  73.                     l.delta[obj_index] = l.noobject_scale * (0 - l.output[obj_index]);  
  74.                     // 然后再做个判断,如果当期 box 计算的 best_iou > 阈值的话,则说明该 box 是有物体的,于是上面哪行计算的 loss 就不算数,因此清零。  
  75.                     if (best_iou > l.thresh) {  
  76.                         l.delta[obj_index] = 0;  
  77.                     }  
  78.                       
  79.                     // 查了查代码,这里是“如果已经训练的图片数量 < 12800 的话则进入循环”,为什么要判断这玩意....  
  80.                     if(*(net.seen) < 12800){  
  81.                         // 单纯的获取“以当前格子中心”为 x, y 的 box 作为 truth box  
  82.                         box truth = {0};  
  83.                         truth.x = (i + .5)/l.w;  
  84.                         truth.y = (j + .5)/l.h;  
  85.                         truth.w = l.biases[2*n]/l.w;  
  86.                         truth.h = l.biases[2*n+1]/l.h;  
  87.                             // 将预测的 tx, ty, tw, th 和 实际box计算得出的 tx',ty', tw', th' 的差存入 l.delta  
  88.                         delta_region_box(truth, l.output, l.biases, n, box_index, i, j, l.w, l.h, l.delta, .01, l.w*l.h);  
  89.                     }  
  90.                 }  
  91.             }  
  92.         }  
  93.         // 下面的循环 30 次中的 30 这个数我看了很久都没找到这个 30 能和什么关联上,于是猜测 30 的含义是:“假设一张图片中最多包含 30 个物体”  
  94.         // 因此下面是“直接遍历一张图片中的所有已标记的物体的中心所在的格子,然后计算 loss”,而不是“遍历那 13*13 个格子后判断当期格子有无物体,然后计算 loss”  
  95.         for(t = 0; t < 30; ++t){  
  96.             // get truth_box's x, y, w, h  
  97.             box truth = float_to_box(net.truth + t*5 + b*l.truths, 1);  
  98.   
  99.             // 如果本格子中不包含任何物体的中心,则跳过  
  100.             if(!truth.x) break;  
  101.             float best_iou = 0;  
  102.             int best_n = 0;  
  103.             // 假设图片被分成了 13 * 13 个格子,那 l.h 和 l.w 就为 13  
  104.             // 于是要遍历所有的格子,因此下面就要循环 13 * 13 次  
  105.             // 也因此,i 和 j 就是真实物品中心所在的格子的“行”和“列”  
  106.             i = (truth.x * l.w);  
  107.             j = (truth.y * l.h);  
  108.             printf("%d %f %d %f\n", i, truth.x*l.w, j, truth.y*l.h);  
  109.             box truth_shift = truth;  
  110.             // 上面获得了 truth box 的 x,y,w,h,这里讲 truth box 的 x,y 偏移到 0,0,记为 truth_shift.x, truth_shift.y,这么做是为了方便计算 iou  
  111.             truth_shift.x = 0;  
  112.             truth_shift.y = 0;  
  113.             printf("index %d %d\n",i, j);  
  114.             // 每个格子会预测 5 个 boxes,因此这里要循环 5 次  
  115.             for(n = 0; n < l.n; ++n){  
  116.                 // 获得预测结果中 box 的 index  
  117.                 int box_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, 0);  
  118.                 // 获得 box 的预测 x, y, w, h,注意都是相对值,不是真实坐标  
  119.                 box pred = get_region_box(l.output, l.biases, n, box_index, i, j, l.w, l.h, l.w*l.h);  
  120.                 // 这里用 anchor box 的值 / l.w 和 l.h 作为预测的 w 和 h  
  121.                 // ps: 我打印了下 l.bias_match,它的值是 1,说明是能走到里面的,而之所以这么做的原因我是这么理解的:  
  122.                 //      在 yolo v2 的论文中提到:预测 box 的 w,h 是根据 anchors 生成(anchors 是用 k-means 聚类得出的最优结果),即:  
  123.                 //          w = exp(tw) * l.biases[2*n]   / l.w  
  124.                 //          h = exp(th) * l.biases[2*n+1] / l.h  
  125.                 //      不过为什么把 exp() 部分省去还有些疑惑,希望有知道原因的大佬能帮忙解答下。  
  126.                 if(l.bias_match){  
  127.                     pred.w = l.biases[2*n]/l.w;  
  128.                     pred.h = l.biases[2*n+1]/l.h;  
  129.                 }  
  130.                 printf("pred: (%f, %f) %f x %f\n", pred.x, pred.y, pred.w, pred.h);  
  131.                 // 上面 truth box 的 x,y 移动到了 0,0 ,因此预测 box 的 x,y 也要移动到 0,0,这么做是为了方便计算 iou  
  132.                 pred.x = 0;  
  133.                 pred.y = 0;  
  134.                 float iou = box_iou(pred, truth_shift);  
  135.                 if (iou > best_iou){  
  136.                     best_iou = iou;  
  137.                     best_n = n;  
  138.                 }  
  139.             }  
  140.             printf("%d %f (%f, %f) %f x %f\n", best_n, best_iou, truth.x, truth.y, truth.w, truth.h);  
  141.   
  142.             // 根据上面的 best_n 找出 box 的 index  
  143.             int box_index = entry_index(l, b, best_n*l.w*l.h + j*l.w + i, 0);  
  144.             // 计算 box 和 truth box 的 iou  
  145.             float iou = delta_region_box(truth, l.output, l.biases, best_n, box_index, i, j, l.w, l.h, l.delta, l.coord_scale *  (2 - truth.w*truth  
  146. .h), l.w*l.h);  
  147.             // 如果 iou > .5,recall +1  
  148.             if(iou > .5) recall += 1;  
  149.             avg_iou += iou;  
  150.   
  151.             //l.delta[best_index + 4] = iou - l.output[best_index + 4];  
  152.             // 根据 best_n 找出 confidence 的 index  
  153.             int obj_index = entry_index(l, b, best_n*l.w*l.h + j*l.w + i, 4);  
  154.             avg_obj += l.output[obj_index];  
  155.             // 因为运行到这里意味着该格子中有物体中心,所以该格子的 confidence 就是 1, 而预测的 confidence 是 l.output[obj_index],所以根据公式有下式  
  156.             l.delta[obj_index] = l.object_scale * (1 - l.output[obj_index]);  
  157.             if (l.rescore) {  
  158.                 // 用 iou 代替上面的 1(经调试,l.rescore = 1,因此能走到这里)  
  159.                 l.delta[obj_index] = l.object_scale * (iou - l.output[obj_index]);  
  160.             }  
  161.   
  162.             // 获得真实的 class  
  163.             int class = net.truth[t*5 + b*l.truths + 4];  
  164.             if (l.map) class = l.map[class];  
  165.             // 获得预测的 class 的 index  
  166.             int class_index = entry_index(l, b, best_n*l.w*l.h + j*l.w + i, 5);  
  167.             // 把所有 class 的预测概率与真实 class 的 0/1 的差 * scale,然后存入 l.delta 里相应 class 序号的位置  
  168.             delta_region_class(l.output, l.delta, class_index, class, l.classes, l.softmax_tree, l.class_scale, l.w*l.h, &avg_cat);  
  169.             ++count;  
  170.             ++class_count;  
  171.         }  
  172.     }  
  173.     printf("\n");  
  174.     // 现在,l.delta 中的每一个位置都存放了 class、confidence、x, y, w, h 的差,于是通过 mag_array 遍历所有位置,计算每个位置的平方的和后开根  
  175.     // 然后利用 pow 函数求平方  
  176.     *(l.cost) = pow(mag_array(l.delta, l.outputs * l.batch), 2);  
  177.     printf("Region Avg IOU: %f, Class: %f, Obj: %f, No Obj: %f, Avg Recall: %f,  count: %d\n", avg_iou/count, avg_cat/class_count, avg_obj/count, a  
  178. vg_anyobj/(l.w*l.h*l.n*l.batch), recall/count, count);  


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多