Simple-fasterrcnn源码学习笔记(4)

这里主要是utils下的部分文件的补充注释


config.py
from pprint import pprint#打印出来更美观
class Config:
    #data
    voc_data_dir='/home/wrc/yuyijie/KITTI/VOCdevkit/VOC2007'
    min_size=600
    max_size=1000
    num_works=8
    test_num_works=8
    rpn_sigma=3.
    roi_sigma=1.
    # for optimizer
    wd=0.0005
    lr_decay=0.1
    lr=1e-3
    #vis
    env='faster-rcnn'
    port=8097#visdom 端口
    plot_every=40
    #preset
    data='voc'
    pretrained_model='vgg16'
    epoch=14
    use_adam=False
    use_chainer=False
    use_drop=False
    #debug
    debug_file='/tmp/debugf'
    test_num=10000
    #model
    load_path=None
    caffe_pretrain=False
    caffe_pretrain_path='checkpoints/vgg16_caffe.pth'
    def _parse(self,kwargs):#解析并设置用户设定的参数
        state_dict=self._state_dict()#读取Config类所有参数dict{para_name:para_value}
        for k,v in kwargs.items():#遍历用户传来的dict
            if k not in state_dict:
                raise ValueError('Unknow option:"--%s"'%k)
            setattr(self,k,v)#设置参数
        print('=============user config=========')
        pprint(self._state_dict())#打印参数
        print('=============end=========')


    def _state_dict(self):
        return {k:getattr(self,k) for k ,_ in Config.__dict__.items() if not k.startswith('_')}
    #字典解析,字典解析,Config.__dict__.items() 取出类中所有的函数、全局变量以及一些内置的属性
  # 前面我们设定的都是全局变量(键值对:比如min_size = 600),没有函数,而系统内置属性都是_打头的,
  # 所以我们要not k.startswith('_')  返回结果dict{para_name0:para_value0,para_name1:para_value1,....}
opt=Config()#创建config对象
array_tool.py
import torch as t
import numpy as np

def tonumpy(data):#将数据转化为Numpy
    if isinstance(data,np.ndarray):#如果数据是numpy类型 直接返回
        return data
    if isinstance(data,t.Tensor):
        return data.detach().cpu().numpy()#将变量从图中分离(使得数据独立,以后你再如何操作都不会对图,对模型产生影响)
    #如果是gpu类型还要转化为cpu,再转化为numpy

def totensor(data,cuda=True):#将数据转化为Tensor或者cuda
    if isinstance(data,np.ndarray):
        tensor=t.from_numpy(data)
    if isinstance(data,t.Tensor):
        tensor=data.detach() #隔离变量
    if cuda:
        tensor=tensor.cuda()
    return tensor

def scalar(data):#取出数据的值
    if isinstance(data,np.ndarray):
        return data.reshape(1)[0]#如果是numpy类型(必须为1个数据 几维都行) 取出这个数据的值
    if isinstance(data,t.Tensor):
        return data.item()#如果是tensor类型 调用pytorch常用的item方法 取出tensor的值
eval_tool.py
from __future__ import division
from collections import defaultdict
#这里的defaultdict(function_factory)构建的是一个类似dictionary的对象,
# 其中keys的值,自行确定赋值,但是values的类型,是function_factory的类实例,而且具有默认值。
# 比如default(int)则创建一个类似dictionary对象,里面任何的values都是int的实例,
# 而且就算是一个不存在的key, d[key] 也有一个默认值,这个默认值是int()的默认值0.
import itertools
import numpy as np
import six
from model.utils.bbox_tools import bbox_iou

def eval_detection_voc(pred_bboxes,pred_labels,pred_scores,gt_bboxes,gt_labels,gt_difficults=None,
                       iou_thresh=0.5,use_07_metric=False):
    #根据PASCAL VOC evaluation
    #所有参数都是list
    # test_num张图片(图片数据来自测试数据testdata)的预测框,标签,分数,和真实的框,标签和分数。所有参数都是list
    # len(list)=opt.test_num(default=10000)
    # pred_boxes: [(A, 4), (B, 4), (C, 4)....共test_num个]
    # 输入源gt_数据
    # 经过train.predict函数预测出的结果框
    # pred_labels[(A,), (B,), (C,)...共test_num个]
    # pred_scores同pred_labels
    # A, B, C, D是由nms决定的个数,即预测的框个数,不确定。
    # gt_bboxes:[(a, 4), (b, 4)....共test_num个]
    # a b...是每张图片标注真实框的个数
    # gt_labels与gt_difficults同理
    prec,rec=calc_detection_voc_prec_rec(#计算每个label类别的准确率和召回率
        pred_bboxes,pred_labels,pred_scores,gt_bboxes,gt_labels,gt_difficults,iou_thresh=iou_thresh
    )
    ap=calc_detection_voc_ap(prec,rec,use_07_metric=use_07_metric)#根据prec和rec计算map和ap
    return {'ap':ap,'map':np.nanmean(ap)}

def calc_detection_voc_prec_rec(pred_bboxes,pred_labels,pred_scores,gt_bboxes,gt_labels,gt_difficults=None,iou_thresh=0.5):
    pred_bboxes=iter(pred_bboxes)#生成迭代器
    pred_labels=iter(pred_labels)#生成迭代器
    pred_scores=iter(pred_scores)#生成迭代器
    gt_bboxes=iter(gt_bboxes)#生成迭代器
    gt_labels=iter(gt_labels)#生成迭代器
    if gt_difficults is None:
        gt_difficults=itertools.repeat(None)#itertools.repeat生成一个重复的迭代器 None是每次迭代获得的数值
    else:
        gt_difficults=iter(gt_difficults)
    n_pos=defaultdict(int)#defaultdict当key不存在时,dict[key]=default(int)=0 default(list)=[]
    score=defaultdict(list)
    match=defaultdict(list)
    for pred_bbox,pred_label,pred_score,gt_bbox,gt_label,gt_difficult in six.moves.zip(pred_bboxes,pred_labels,
                                                                pred_scores,gt_bboxes,gt_labels,gt_difficults):
        if gt_difficults is None:
            gt_difficults=np.zeros(gt_bbox.shape[0],dtype=bool)#全部设置为非difficult
        #遍历一边图片中,所有出现的label
        for l in np.unique(np.concatenate((pred_label,gt_label)).astype(int)):
            #拼接后返回无重复的从小到大排序的一维numpy 如[2,3,4,5,6]
            # 并遍历这个一维数组,即遍历这张图片出现过的标签数字(gt_label+pred_label)
            #>>> np.unique([1, 1, 2, 2, 3, 3])
            # array([1, 2, 3])
            # >>> a = np.array([[1, 1], [2, 3]])
            # >>> np.unique(a)
            # array([1, 2, 3])
            pred_mask_l=pred_label==l#//广播pred_mask_l=[eg. T,F,T,T,F,F,F,T..] 所有预测label中等于L的为T 否则F
            pred_bbox_l=pred_bbox[pred_mask_l]#选出label=L的所有pre_box
            pred_score_l=pred_scores[pred_mask_l]#label=L 对应所有pre_score
            #sort by score
            order=pred_score_l.argsort()[::-1]#获得score降序排序索引
            pred_bbox_l=pred_bbox_l[order]
            pred_score_l=pred_score_l[order]

            gt_mask_l=gt_label==l#同理
            gt_bbox_l=gt_bbox[gt_mask_l]
            gt_difficult_l=gt_difficult[gt_mask_l]

            n_pos[l]+=np.logical_not(gt_difficult_l).sum()#对T,F取反求和,统计出difficult=0的个数
            score[l].extend(pred_score_l)#score={l:predscore_l,...} extend是针对defaultdict中是list的情况

            if len(pred_bbox_l)==0:#没有预测的label=L的box,即真实label有L,我们全没有预测到
                continue#跳过这张图片 此时没有对match字典操作,之前score[l].extend操作也为空 保持了match和score的形状一致
            if len(gt_bbox_l)==0:#没有真实的label=L的情况 即预测中有L 真实中没有 全都预测错了
                match[l].extend((0,)*pred_bbox_l.shape[0])#match{L:[0,0,0...n_pred_box个0]}
            # VOC evaluation follows integer typed bounding boxes.
            # 作者给的注释是follows integer typed bounding boxes
            # 但是只改变了ymax, xmax的值,重要的是这样做并不能转化为整数
            # pred_bbox和gt_bbox只
            # 参与了IOU计算且后面没有参与其他计算
            pred_bbox_l=pred_bbox_l.copy()
            pred_bbox_l[:,2:]+=1#ymax,xmax+=1
            gt_bbox_l=gt_bbox_l.copy()
            gt_bbox_l[:,2:]+=1

            iou=bbox_iou(pred_bbox_l,gt_bbox_l)#计算两个box的iou
            gt_index=iou.argmax(axis=1)#有len(pred_bbox_l)个索引,第i个索引值n表示gt_box[n]与pred_box[i]iou最大
            #比如 4个pred_box 3 个gtbox
            #     gt0 gt1 gt2    最大对应用*表示   这里的gt_index输出将是[1,0,0,2] 第0个索引值1 表示gt1和pred_box[0]iou最大
            #  A       *
            #  B   *
            #  C   *
            #  D           *
            #要注意 这里的gt_index是会重复的 因为同一个gt会与很多pred_box拥有最大iou
            #    #计算iou 将会是(N,K)纬度的输出,如果所有tl都大于br的话
            gt_index[iou.max(axis=1)<iou_thresh]=-1
            #这里则是在上面的基础上把小于阈值的gt_index设置为-1
            #将gt_box与pred_box iou<thresh的索引值置为-1
             # 即针对每个pred_bbox,与每个gt_bbox IOU的最大值 如果最大值小于阀值则置为-1
             # 即我们预测的这个box效果并不理想 后续会将index=-1的 matchlabel=0
            del iou
            selec=np.zeros(gt_bbox_l.shape[0],dtype=bool)
            for gt_idx in gt_index:#遍历gt_index索引值
                if gt_index>=0:#即iou满足条件的bbox
                    if gt_difficult_l[gt_idx]:#对应的gt_difficult=1即困难标记
                        match[l].append(-1)#match[l] 后面追加一个-1
                    else:
                        #这里的做法可能有个问题 就是比如gt1同时根B,C两个prebox拥有超过阈值的iou这时候的排序就很关键 ,要先按照分数来进行排序
                        if not selec[gt_idx]:#没有被选国,select[idx]=0的时候
                            match[l].append(1)
                        else:#对应的gt_box已经被选国一次,即已经和前面的某个pre_box iou最大
                            match[l].append(0)
                    selec[gt_idx]=True#一个gt被选过之后,则要设置为True
                else:#不满足iou>thresh
                    match[l].append(0)#追加0 很重复匹配的结果一样
    #我们注意到上面为每个pred_box都打了label 0, 1, -1
    #len(match[l]) = len(score[l]) = len(pred_bbox_l)
    for iter_ in ( # 上面的 six.moves.zip遍历会在某一iter遍历到头后停止,由于pred_bboxes等是全局iter对象,
    #我们此时继续调用next取下一数据,如果有任一数据不为None,那么说明他们的len是不相等的 有悖常理,数据错误
    pred_bboxes,pred_labels,pred_scores,gt_bboxes,gt_labels,gt_difficult):
        if next(iter_,None) is not None:#next(iter_,None)表示调用next 如果已经遍历到了头 不抛出异常而是返回None
            raise ValueError("Length of input iterables need to be same")

    n_fg_class=max(n_pos.keys())+1#有n_fg_class个类
    prec=[None]*n_fg_class
    rec=[None]*n_fg_class
    for l in n_pos.keys():#遍历所有label
        score_l=np.array(score[l])
        match_l=np.array(match[l],dtype=np.int8)
        order=score_l.argsort()[::-1]
        match_l=match_l[order]#对应match按照score由大到小排序

        tp=np.cumsum(match_l==1)#统计累计match_1=1的个数
        # 比如 match_l =[1,1,1,0,1,1] np.cumsum(match)=[1,2,3,3,4,5]
        # tp=[1,2,3,3,4,5] fp=[0,0,0,1,1,1] 长度是所有predbox的总数 prec[l]=[1,1,1,0.75,0.8,0.833]
        fp=np.cumsum(match_l==0)
        prec[l]=tp/(tp+fp)
        if n_pos[l]>0:#如果n_pos[l]=0 那么rec[l]=None
            rec[l]=tp/n_pos[l]#这里干嘛不用所有的gt[l]的总数
    return prec,rec
def calc_detection_voc_ap(prec,rec,use_07_metric=False):
    n_fg_class=len(prec)
    ap=np.empty(n_fg_class)
    for l in six.moves.range(n_fg_class):#遍历每个label
        if prec[l] is None or rec[l] is None:#如果为NOne 则ap设置为np.nan
            ap[l]=np.nan
            continue
        if use_07_metric:
            # 11 point metric
            ap[l]=0
            for t in np.arange(0.,1.1,0.1):#t=0 0.1 0.2...1.0
                if np.sum(rec[l]>=t)==0:#这个标签召回率没有大于阈值的
                    p=0
                else:
                    p=np.max(np.nan_to_num(prec[l])[rec[l]>=t])#p=(rec>=t时,对应index:prec中的最大值) np.nan_to_num是为了
                    #让None=0一边计算
                ap[l]+=p/11
        else:
            mpre=np.concatenate(([0],np.nan_to_num(prec[l]),[0]))#头尾添加0
            mrec=np.concatenate(([0],rec[l],[1]))#头添加0 尾添加1
            mpre=np.maximum.accumulate(mpre[::-1])[::-1]#获得从小到大的累计最大值
            #>>> np.add.accumulate([2, 3, 5])
            # array([ 2,  5, 10])
            # >>> np.multiply.accumulate([2, 3, 5])
            # array([ 2,  6, 30])
            # 我们知道
            # 我们是按score由高到低排序的
            # 而且我们给box打了label
            # 0, 1, -1
            # score高时1的概率会大,所以pre是累计降序的
            # 而rec是累积升序的,那么此时将pre倒序再maxuim.ac
            # 获得累积最大值,再倒序后
            # 从小到大排序的累积最大值
            i=np.where(mrec[1:]!=mrec[:-1])[0]#差位比较,看哪里改变了recall的值,记录index(x轴)
            #and sum (\Delta recall)*prec
            ap[l]=np.sum((mrec[i+1]-mrec[i])*mrec[i+1])#差值*mpre_max的值,(x轴之差*ymax)
    return ap
vis_tool.py
import time

import numpy as np
import matplotlib
import torch as t
import visdom

matplotlib.use('Agg')
from matplotlib import pyplot as plot

# from data.voc_dataset import VOC_BBOX_LABEL_NAMES


VOC_BBOX_LABEL_NAMES = (
    'fly',
    'bike',
    'bird',
    'boat',
    'pin',
    'bus',
    'c',
    'cat',
    'chair',
    'cow',
    'table',
    'dog',
    'horse',
    'moto',
    'p',
    'plant',
    'shep',
    'sofa',
    'train',
    'tv',
)
def vis_image(img,ax=None):#img是经过你标准化的0-255的rgb图像 ax是传来的matplotlib.axes,Axis对象,告诉我们画在那里
    if ax is None:#如果没有这个对象 创建一个(1,1,1)的
        fig=plot.figure()
        ax=fig.add_subplot(1,1,1)
    #CHW-》HWC
    img=img.transpose(1,2,0)
    ax.imshow(img.astype(np.uint8))#matplotlib 支持0-1的图像显示,也支持0-255的图像显示,转成uint8是为了告诉它我们的图像是0-255的
    return ax #返回axis对象后续使用,vis_box会继续调用,在此图片基础上画框等

def vis_bbox(img,bbox,label=None,score=None,ax=None):
    label_names=list(VOC_BBOX_LABEL_NAMES)+['bg']

    if label is not None and not len(bbox)==len(label):#框个数不等于标签个数
        raise ValueError('The length of label must be same as that of bbox')
    if score is not None and not len(bbox)==len(score):#score个数不等于标签个数
        raise ValueError('The length of score must be same as that of bbox')
    ax=vis_image(img,ax=ax)
    if len(bbox)==0:#没有框的话
        return ax
    for i,bb in enumerate(bbox):#遍历一张图中所有的框
        xy=(bb[1],bb[0])#左上角
        height=bb[2]-bb[0]
        width=bb[3]-bb[1]
        ax.add_patch(plot.Rectangle(xy,width,height,fill=False,edgecolor='red',linewidth=2))#画矩形
        caption=list()
        if label is not None and label_names is not None:
            lb=label[i]#此框对应的label
            if not (-1<=lb<len(label_names)):#label名字超过边界
                raise ValueError('No corresponding name is given')
            caption.append(label_names[lb])#物体名字加入caption list

        if score is not None:
            sc=score[i] #找到此框对应分数
            caption.append('{:.2f}'.format(sc))#加入caption list
        if len(caption)>0:
            ax.text(bb[1],bb[0],':'.join(caption),style='italic',bbox={'facecolor':'white','alpha':0.5,'pad':0})#bbox是框住字体的框的一些参数
        return ax
def fig2data(fig):#将matplotlib的figure图像 转化为RGBA形式的NUmpy返回
    fig.canvas.draw()#画图,渲染 fig.canvas.draw()重绘所有内容。这是你的瓶颈。就你而言,你不需要重新绘制轴边界,刻度标签等等
    w,h=fig.canvas.get_width_height()#得到figure的w,h
    buf=np.fromstring(fig.canvas.tostring_argb(),dtype=np.uint8)#得到numpy形式的argb图像
    buf.shape=(w,h,4)#(w,h,argb)
    buf=np.roll(buf,3,axis=2)#(w,h,rgba) 在第二轴水平向右滚动3个距离
    return buf.reshape(h,w,4)#(h,w,4)
def fig4vis(fig):#将matplotlib的figure图像转化为pytorch 常用的3通道RGB CHW 0-1图像
    ax=fig.get_figure()
    img_data=fig2data(ax).astype(np.int32)
    plot.close()
    return img_data[:,:,:3].transpose((2,0,1))/255
    #(H,W,RGBA)-》(H,W,RGB)-》(RGB,H,W)即CHW/255 -》0-1

def visdom_bbox(*args,**kwargs):#我们要使用的函数 包含上面所有的函数调用,传入img box score 画成一张图像 以numpy的形式返回
    fig=vis_bbox(*args,**kwargs)
    data=fig4vis(fig)
    return data

class Visualizer(object):  #Visdom可视化部分
    def __init__(self, env='default', **kwargs):
        self.vis = visdom.Visdom(env=env, use_incoming_socket=False, **kwargs) #visdom对象
        self._vis_kw = kwargs  #参数
        self.index = {}
        self.log_text = ''

    def reinit(self, env='default', **kwargs):
       #可能由于用户要改变kwargs初始化参数 而重新初始化visdom对象
        self.vis = visdom.Visdom(env=env, **kwargs)
        return self

    def plot_many(self, d):#plot
        #参数 d: dict (name,value) i.e. ('loss',0.11)
        for k, v in d.items():
            if v is not None:
                self.plot(k, v) #调用下面的plot函数 画折线图(各种损失折线图,训练map折线图)

    def img_many(self, d):    #参数 d: dict (name,value) i.e. ('loss',0.11)
        for k, v in d.items():
            self.img(k, v)  #调用下面的img函数 画图

    def plot(self, name, y, **kwargs): #画一条直线  title=name
        x = self.index.get(name, 0)
        self.vis.line(Y=np.array([y]), X=np.array([x]),
                      win=name,
                      opts=dict(title=name),
                      update=None if x == 0 else 'append',
                      **kwargs
                      )
        self.index[name] = x + 1

    def img(self, name, img_, **kwargs): #画图片  title=name
        self.vis.images(t.Tensor(img_).cpu().numpy(),
                        win=name,
                        opts=dict(title=name),
                        **kwargs
                        )

    def log(self, info, win='log_text'): #打印log日志  时间+info
        self.log_text += ('[{time}] {info} <br>'.format(
            time=time.strftime('%m%d_%H%M%S'), \
            info=info))
        self.vis.text(self.log_text, win)

    def __getattr__(self, name): #按name获得一个属性
        return getattr(self.vis, name)

    def state_dict(self): #返回现有参数 dict
        return {
            'index': self.index,
            'vis_kw': self._vis_kw,
            'log_text': self.log_text,
            'env': self.vis.env
        }

    def load_state_dict(self, d):  #参数 d: dict (name,value) i.e. ('loss',0.11)  初始化参数
        self.vis = visdom.Visdom(env=d.get('env', self.vis.env), **(self.d.get('vis_kw')))
        self.log_text = d.get('log_text', '')
        self.index = d.get('index', dict())
        return self
不熟悉的numpy,pytorch,python操作
data.detach()

将tensor变量从图中分离(使得数据独立,以后你再如何操作都不会对图,对模型产生影响)

data.numpy()

tensor2numpy

t.from_numpy(data)

numpy2tensor

data.reshape(1)[0]

如果是numpy类型(必须为1个数据 几维都行) 取出这个数据的值

data.item()

如果是tensor类型 调用pytorch常用的item方法 取出tensor的值

from collections import defaultdict

这里的defaultdict(function_factory)构建的是一个类似dictionary的对象,
其中keys的值,自行确定赋值,但是values的类型,是function_factory的类实例,而且具有默认值。 比如default(int)则创建一个类似dictionary对象,里面任何的values都是int的实例,
而且就算是一个不存在的key, d[key] 也有一个默认值,这个默认值是int()的默认值0.
这在计算map等eval指标的时候非常有用
比如
if not selec[gt_idx]:#没有被选国,select[idx]=0的时候 match[l].append(1)
就能把所有的1都放到l对应的list中 而且即使没有对应的值,该list也会存在只是为空
{‘8’:[1,1,1,1]}这种形式

np.unique
#拼接后返回无重复的从小到大排序的一维numpy 如[2,3,4,5,6]
           # 并遍历这个一维数组,即遍历这张图片出现过的标签数字(gt_label+pred_label)
           #>>> np.unique([1, 1, 2, 2, 3, 3])
           # array([1, 2, 3])
           # >>> a = np.array([[1, 1], [2, 3]])
           # >>> np.unique(a)
           # array([1, 2, 3])
iter(pred_bboxes)#生成迭代器

把一个list可以变成迭代器,相比直接用list去做循环能节省很多内存
迭代器的节省内存的原因:
一个是生成器函数,外表看上去像是一个函数,但是没有用return语句一次性的返回整个结果对象列表,取而代之的是使用yield语句一次返回一个结果。另一个是生成器表达式,类似于上一小节的列表解析,但是方括号换成了圆括号,他们返回按需产生的一个结果对象,而不是构建一个结果列表。

生成器函数他通过yield关键字返回一个值后,还能从其退出的地方继续运行,因此可以随时间产生一系列的值。他们自动实现了迭代协议,并且可以出现在迭代环境中。运行的过程是这样的:生成器函数返回一个迭代器,for循环等迭代环境对这个迭代器不断调用next函数,不断的运行到下一个yield语句,逐一取得每一个返回值,直到没有yield语句可以运行,最终引发StopIteration异常。

np.logical_not()

输入一组bool型的数据
对T,F取反

np.cumsum(match_l==1)#统计累计match_1=1的个数

cumsum(a, axis=None, dtype=None, out=None):
Axis along which the cumulative sum is computed. The default
(None) is to compute the cumsum over the flattened array.

Return the cumulative sum of the elements along a given axis.
比如 match_l =[1,1,1,0,1,1] np.cumsum(match==1)=[1,2,3,3,4,5]
tp=[1,2,3,3,4,5] fp=[0,0,0,1,1,1] 长度是所有predbox的总数 prec[l]=[1,1,1,0.75,0.8,0.833]

Examples
 --------
 >>> a = np.array([[1,2,3], [4,5,6]])
 >>> a
 array([[1, 2, 3],
        [4, 5, 6]])
 >>> np.cumsum(a)
 array([ 1,  3,  6, 10, 15, 21])
 >>> np.cumsum(a, dtype=float)     # specifies type of output value(s)
 array([  1.,   3.,   6.,  10.,  15.,  21.])

 >>> np.cumsum(a,axis=0)      # sum over rows for each of the 3 columns
 array([[1, 2, 3],
        [5, 7, 9]])
 >>> np.cumsum(a,axis=1)      # sum over columns for each of the 2 rows
 array([[ 1,  3,  6],
        [ 4,  9, 15]])

 """
np.nan_to_num

Replace NaN with zero and infinity with large finite numbers

--------
 >>> np.nan_to_num(np.inf)
 1.7976931348623157e+308
 >>> np.nan_to_num(-np.inf)
 -1.7976931348623157e+308
 >>> np.nan_to_num(np.nan)
 0.0
 >>> x = np.array([np.inf, -np.inf, np.nan, -128, 128])
 >>> np.nan_to_num(x)
 array([ 1.79769313e+308, -1.79769313e+308,  0.00000000e+000, # may vary
        -1.28000000e+002,  1.28000000e+002])
 >>> np.nan_to_num(x, nan=-9999, posinf=33333333, neginf=33333333)
 array([ 3.3333333e+07,  3.3333333e+07, -9.9990000e+03, 
        -1.2800000e+02,  1.2800000e+02])
 >>> y = np.array([complex(np.inf, np.nan), np.nan, complex(np.nan, np.inf)])
 array([  1.79769313e+308,  -1.79769313e+308,   0.00000000e+000, # may vary
      -1.28000000e+002,   1.28000000e+002])
 >>> np.nan_to_num(y)
 array([  1.79769313e+308 +0.00000000e+000j, # may vary
          0.00000000e+000 +0.00000000e+000j,
          0.00000000e+000 +1.79769313e+308j])
 >>> np.nan_to_num(y, nan=111111, posinf=222222)
 array([222222.+111111.j, 111111.     +0.j, 111111.+222222.j])
 """
np.maximum.accumulate(mpre[::-1])[::-1]

获得从小到大的累计最大值

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值