Navigation

    Gpushare.com

    • Register
    • Login
    • Search
    • Popular
    • Categories
    • Recent
    • Tags
    1. Home
    2. Violet_恒源智享云
    • Profile
    • Following 0
    • Followers 2
    • Topics 57
    • Posts 84
    • Best 66
    • Groups 1

    Violet_恒源智享云

    @Violet_恒源智享云

    96
    Reputation
    31
    Profile views
    84
    Posts
    2
    Followers
    0
    Following
    Joined Last Online

    Violet_恒源智享云 Unfollow Follow
    administrators

    Best posts made by Violet_恒源智享云

    • 【GiantPandaCV】基于Caffe格式部署YOLOV5模型

      【GiantPandaCV导语】本文为大家介绍了一个caffe部署yolov5 模型的教程,并开源了全部代码。主要是教你如何搭建caffe推理环境,对yolov5模型做onnx格式转换,onnx模型转caffe模型,实测在1070显卡做到了11ms一帧!

      部署简介

      如果说目标检测落地最广的是哪个算法,yolo系列肯定有一席之地,本文为大家介绍yolov5s 4.0模型如何转换为caffe模型并推理,据我所知,华为海思NNIE只支持caffe模型的转换,所以yolov5模型要想在海思芯片上部署,转换为caffe模型是有必要的(在我的1070显卡上,yolov5s 4.0 的模型inference做到了11ms一帧!)


      推理速度截图

      环境配置

      • ubuntu:18.04

      • cuda:10.0

      • cudnn:7.6.5

      • caffe: 1.0

      • OpenCV:3.4.2

      • Anaconda3:5.2.0

      • 相关的安装包我已经放到百度云盘,可以从如下链接下载: https://pan.baidu.com/s/17bjiU4H5O36psGrHlFdM7A 密码: br7h

      • cuda和cudnn的安装

      • 可以参考我的TensorRT量化部署yolov5模型的文章(https://zhuanlan.zhihu.com/p/348110519)

      • Anaconda安装

      • chmod +x Anaconda3-5.2.0-Linux-x86_64.sh(从上面百度云盘链接下载) .- /Anaconda3-5.2.0-Linux-x86_64.sh

      • 按ENTER,然后按q调至结尾

      • 接受协议 yes

      • 安装路径 使用默认路径

      • 执行安装

      • 在使用的用户.bashrc上添加anaconda路径,比如

      • export PATH=/home/willer/anaconda3/bin:$PATH

      • caffe安装

      • git clone https://github.com/Wulingtian/yolov5_caffe.git

      • cd yolov5_caffe

      • 命令行输入如下内容:

      • export CPLUS_INCLUDE_PATH=/home/你的用户名/anaconda3/include/python3.6m

      • make all -j8

      • make pycaffe -j8

      • vim ~/.bashrc

      • export PYTHONPATH=/home/你的用户名/yolov5_caffe/python:$PYTHONPATH

      • source ~/.bashrc

      编译过程踩过的坑

      libstdc++.so.6: version `GLIBCXX_3.4.21’ not found
      解决方案:https://blog.csdn.net/phdsky/article/details/84104769?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.add_param_isCf&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.add_param_isCf#commentBox

      ImportError: No module named google.protobuf.internal
      解决方案:https://blog.csdn.net/quantum7/article/details/83507364
      wrap_python.hpp:50:23: fatal error: pyconfig.h: No such file or dir

      解决方案:https://blog.csdn.net/weixin_37251044/article/details/79158823

      yolov5s模型转换onnx模型

      • pip安装onnx和onnx-simplifier
      • pip install onnx
      • pip install onnx-simplifier
      • 拉取yolov5官方代码
      • git clone https://github.com/ultralytics/yolov5.git
      • 训练自己的模型步骤参考yolov5官方介绍,训练完成后我们得到了一个模型文件
      • cd yolov5
      • python models/export.py --weights 训练得到的模型权重路径 --img-size 训练图片输入尺寸
      • python -m onnxsim onnx模型名称 yolov5s-simple.onnx 得到最终简化后的onnx模型

      onnx模型转换caffe模型

      • git clone https://github.com/Wulingtian/yolov5_onnx2caffe.git
      • cd yolov5_onnx2caffe v- im convertCaffe.py
      • 设置onnx_path(上面转换得到的onnx模型),prototxt_path(caffe的prototxt保存路径),caffemodel_path(caffe的caffemodel保存路径)
      • python convertCaffe.py 得到转换好的caffe模型

      caffe模型推理

      • 定位到yolov5_caffe目录下
      • cd tools
      • vim caffe_yolov5s.cpp
      • 设置如下参数:
      • INPUT_W(模型输入宽度)
      • INPUT_H(模型输入高度)
      • NUM_CLASS(模型有多少个类别,例如我训练的模型是安全帽检测,只有1类,所以设置为1,不需要加背景类)
      • NMS_THRESH(做非极大值抑制的阈值)
      • CONF_THRESH(类别置信度)
      • prototxt_path(caffe模型的prototxt路径)
      • caffemodel_path(caffe模型的caffemodel路径)
      • pic_path(预测图片的路径)
      • 定位到yolov5_caffe目录下
      • make -j8
      • cd build
      • ./tools/caffe_yolov5s 输出平均推理时间,以及保存预测图片到当前目录下,至此,部署完成!

      华为海思NNIE部署拙见

      如果有小伙伴,想把caffe模型部署到海思芯片,建议把yolov5的focus层替换为conv层(stride为2),upsample层替换为deconv层,如下图所示修改:

      修改后的模型配置yaml文件

      预测图片展示


      预测效果展示

      ————————————————————————————
      转载来源:公众号【GiantPandaCV】
      公众号简介:专注于机器学习、深度学习、计算机视觉、图像处理等多个方向技术分享。团队由一群热爱技术且热衷于分享的小伙伴组成。我们坚持原创,每周一到两篇原创技术分享。希望在传播知识、分享知识的同时能够启发你,大家一起共同进步(・ω<)☆

      posted in CV领域
      Violet_恒源智享云
      Violet_恒源智享云
    • 【GiantPandaCV】【从零开始学深度学习编译器】一,深度学习编译器及TVM 介绍

      0x0. 介绍

      大家好呀,在过去的半年到一年时间里,我分享了一些算法解读,算法优化,模型转换相关的一些文章。这篇文章是自己开启学习深度学习编译器的第一篇文章,后续也会努力更新这个系列。这篇文章是开篇,所以我不会太深入讲解TVM的知识,更多的是介绍一下深度学习编译器和TVM是什么?以及为什么我要选择学习TVM,最后我也会给出一个让读者快速体验TVM效果的一个开发环境搭建的简要教程以及一个简单例子。

      0x1. 为什么需要深度学习编译器?

      深度学习编译器这个词语,我们可以先拆成两个部分来看。

      首先谈谈深度学习领域。从训练框架角度来看,Google的TensorFlow和FaceBook的Pytorch是全球主流的深度学习框架,另外亚马逊的MxNet,百度的Paddle,旷视的MegEngine,华为的Mindspore以及一流科技的OneFlow也逐渐在被更多人接受和使用。这么多训练框架,我们究竟应该选择哪个?如果追求易用性,可能你会选择Pytorch,如果追求项目部署落地,可能你会选择TensorFlow,如果追求分布式训练最快可能你会体验OneFlow。所以这个选择题没有确定答案,在于你自己的喜好。从推理框架角度来看,无论我们选择何种训练框架训练模型,我们最终都是要将训练好的模型部署到实际场景的,在模型部署的时候我们会发现我们要部署的设备可能是五花八门的,例如Intel CPU/Nvidia GPU/Intel GPU/Arm CPU/Arm GPU/FPGA/NPU(华为海思)/BPU(地平线)/MLU(寒武纪),如果我们要手写一个用于推理的框架在所有可能部署的设备上都达到良好的性能并且易于使用是一件非常困难的事。

      一般要部署模型到一个指定设备上,我们一般会使用硬件厂商自己推出的一些前向推理框架,例如在Intel的CPU/GPU上就使用OpenVINO,在Arm的CPU/GPU上使用NCNN/MNN等,在Nvidia GPU上使用TensorRT。虽然针对不同的硬件设备我们使用特定的推理框架进行部署是最优的,但这也同时存在问题,比如一个开发者训练了一个模型需要在多个不同类型的设备上进行部署,那么开发者需要将训练的模型分别转换到特定框架可以读取的格式,并且还要考虑各个推理框架OP实现是否完全对齐的问题,然后在不同平台部署时还容易出现的问题是开发者训练的模型在一个硬件上可以高效推理,部署到另外一个硬件上性能骤降。并且从之前几篇探索ONNX的文章来看,不同框架间模型转换工作也是阻碍各种训练框架模型快速落地的一大原因。

      接下来,我们要简单描述一下编译器。实际上在编译器发展的早期也和要将各种深度学习训练框架的模型部署到各种硬件面临的情况一下,历史上出现了非常多的编程语言,比如C/C++/Java等等,然后每一种硬件对应了一门特定的编程语言,再通过特定的编译器去进行编译产生机器码,可以想象随着硬件和语言的增多,编译器的维护难度是多么困难。还好现代的编译器已经解决了这个问题,那么这个问题编译器具体是怎么解决的呢?

      为了解决上面的问题,科学家为编译器抽象出了编译器前端,编译器中端,编译器后端等概念,并引入IR (Intermediate Representation)的概率。解释如下:

      • 编译器前端:接收C/C++/Java等不同语言,进行代码生成,吐出IR
      • 编译器中端:接收IR,进行不同编译器后端可以共享的优化,如常量替换,死代码消除,循环优化等,吐出优化后的IR
      • 编译器后端:接收优化后的IR,进行不同硬件的平台相关优化与硬件指令生成,吐出目标文件

      以LLVM编译器为例子,借用蓝色(知乎ID)大佬的图:


      编译器抽象

      受到编译器解决方法的启发,深度学习编译器被提出,我们可以将各个训练框架训练出来的模型看作各种编程语言,然后将这些模型传入深度学习编译器之后吐出IR,由于深度学习的IR其实就是计算图,所以可以直接叫作Graph IR。针对这些Graph IR可以做一些计算图优化再吐出IR分发给各种硬件使用。这样,深度学习编译器的过程就和传统的编译器类似,可以解决上面提到的很多繁琐的问题。仍然引用蓝色大佬的图来表示这个思想。


      深度学习编译器抽象

      0x02. TVM

      基于上面深度学习编译器的思想,陈天奇领衔的TVM横空出世。TVM就是一个基于编译优化的深度学习推理框架(暂且说是推理吧,训练功能似乎也开始探索和接入了),我们来看一下TVM的架构图。来自于:https://tvm.apache.org/2017/10/06/nnvm-compiler-announcement


      TVM架构图

      从这个图中我们可以看到,TVM架构的核心部分就是NNVM编译器(注意一下最新的TVM已经将NNVM升级为了Realy,所以后面提到的Relay也可以看作是NNVM)。NNVM编译器支持直接接收深度学习框架的模型,如TensorFlow/Pytorch/Caffe/MxNet等,同时也支持一些模型的中间格式如ONNX、CoreML。这些模型被NNVM直接编译成Graph IR,然后这些Graph IR被再次优化,吐出优化后的Graph IR,最后对于不同的后端这些Graph IR都会被编译为特定后端可以识别的机器码完成模型推理。比如对于CPU,NNVM就吐出LLVM可以识别的IR,再通过LLVM编译器编译为机器码到CPU上执行。

      0x03. 环境配置

      工欲善其事,必先利其器,再继续探索TVM之前我们先了解一下TVM的安装流程。这里参考着官方的安装文档提供两种方法。

      0x03.1 基于Docker的方式

      我们可以直接拉安装配置好TVM的docker,在docker中使用TVM,这是最快捷最方便的。例如拉取一个编译了cuda后端支持的TVM镜像,并启动容器的示例如下:

      docker pull tvmai/demo-gpu
      nvidia-docker run --rm -it tvmai/demo-gpu bash
      

      这样就可以成功进入配置好tvm的容器并且使用TVM了。

      0x03.2 本地编译以Ubuntu为例

      如果有修改TVM源码或者给TVM贡献的需求,可以本地编译TVM,以Ubuntu为例编译和配置的流程如下:

      git clone --recursive https://github.com/apache/tvm tvm
      cd tvm
      mkdir build
      cp cmake/config.cmake build
      cd build
      cmake ..
      make -j4
      export TVM_HOME=/path/to/tvm
      export PYTHONPATH=$TVM_HOME/python:${PYTHONPATH}
      

      这样我们就配置好了TVM,可以进行开发和测试了。

      我的建议是本地开发和调试使用后面的方式,工业部署使用Docker的方式。

      0x04. 样例展示

      在展示样例前说一下我的环境配置,pytorch1.7.0 && TVM 0.8.dev0

      这里以Pytorch模型为例,展示一下TVM是如何将Pytorch模型通过Relay(可以理解为NNVM的升级版,)构建TVM中的计算图并进行图优化,最后再通过LLVM编译到Intel CPU上进行执行。最后我们还对比了一下基于TVM优化后的Relay Graph推理速度和直接使用Pytorch模型进行推理的速度。这里是以torchvision中的ResNet18为例子,结果如下:

      Relay top-1 id: 282, class name: tiger cat
      Torch top-1 id: 282, class name: tiger cat
      Relay time:  1.1846002000000027 seconds
      Torch time:  2.4181047000000007 seconds
      

      可以看到在预测结果完全一致的情况下,TVM能带来2倍左右的加速。这里简单介绍一下代码的流程。这个代码可以在这里(https://github.com/BBuf/tvm_learn)找到。

      0x04.1 导入TVM和Pytorch并加载ResNet18模型

      import time
      import tvm
      from tvm import relay
      
      import numpy as np
      
      from tvm.contrib.download import download_testdata
      
      # PyTorch imports
      import torch
      import torchvision
      
      ######################################################################
      # Load a pretrained PyTorch model
      # -------------------------------
      model_name = "resnet18"
      model = getattr(torchvision.models, model_name)(pretrained=True)
      model = model.eval()
      
      # We grab the TorchScripted model via tracing
      input_shape = [1, 3, 224, 224]
      input_data = torch.randn(input_shape)
      scripted_model = torch.jit.trace(model, input_data).eval()
      

      需要注意的是Relay在解析Pytorch模型的时候是解析TorchScript格式的模型,所以这里使用torch.jit.trace跑一遍原始的Pytorch模型并导出TorchScript模型。

      0x04.2 载入测试图片

      加载一张测试图片,并执行一些后处理过程。

      from PIL import Image
      
      img_url = "https://github.com/dmlc/mxnet.js/blob/main/data/cat.png?raw=true"
      img_path = download_testdata(img_url, "cat.png", module="data")
      img = Image.open(img_path).resize((224, 224))
      
      # Preprocess the image and convert to tensor
      from torchvision import transforms
      
      my_preprocess = transforms.Compose(
          [
              transforms.Resize(256),
              transforms.CenterCrop(224),
              transforms.ToTensor(),
              transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
          ]
      )
      img = my_preprocess(img)
      # 新增Batch维度
      img = np.expand_dims(img, 0)v
      

      0x04.3 Relay导入TorchScript模型并编译到LLVM后端

      接下来我们将PyTorch的graph导入到Relay成为Relay Graph,这里输入层的名字可以任意指定。然后将Gpath使用给定的配置编译到LLVM目标硬件上。

      ######################################################################
      # Import the graph to Relay
      # -------------------------
      # Convert PyTorch graph to Relay graph. The input name can be arbitrary.
      input_name = "input0"
      shape_list = [(input_name, img.shape)]
      mod, params = relay.frontend.from_pytorch(scripted_model, shape_list)
      
      ######################################################################
      # Relay Build
      # -----------
      # Compile the graph to llvm target with given input specification.
      target = "llvm"
      target_host = "llvm"
      ctx = tvm.cpu(0)
      with tvm.transform.PassContext(opt_level=3):
          lib = relay.build(mod, target=target, target_host=target_host, params=params)
      

      0x04.4 在目标硬件上进行推理并输出分类结果

      这里加了一个计时函数用来记录推理的耗时情况。

      ######################################################################
      # Execute the portable graph on TVM
      # ---------------------------------
      # Now we can try deploying the compiled model on target.
      from tvm.contrib import graph_runtime
      
      tvm_t0 = time.clock()
      for i in range(10):
          dtype = "float32"
          m = graph_runtime.GraphModule(lib["default"](ctx))
          # Set inputs
          m.set_input(input_name, tvm.nd.array(img.astype(dtype)))
          # Execute
          m.run()
          # Get outputs
          tvm_output = m.get_output(0)
      tvm_t1 = time.clock()
      

      接下来我们在1000类的字典里面查询一下Top1概率对应的类别并输出,同时也用Pytorch跑一下原始模型看看两者的结果是否一致和推理耗时情况。

      #####################################################################
      # Look up synset name
      # -------------------
      # Look up prediction top 1 index in 1000 class synset.
      synset_url = "".join(
          [
              "https://raw.githubusercontent.com/Cadene/",
              "pretrained-models.pytorch/master/data/",
              "imagenet_synsets.txt",
          ]
      )
      synset_name = "imagenet_synsets.txt"
      synset_path = download_testdata(synset_url, synset_name, module="data")
      with open(synset_path) as f:
          synsets = f.readlines()
      
      synsets = [x.strip() for x in synsets]
      splits = [line.split(" ") for line in synsets]
      key_to_classname = {spl[0]: " ".join(spl[1:]) for spl in splits}
      
      class_url = "".join(
          [
              "https://raw.githubusercontent.com/Cadene/",
              "pretrained-models.pytorch/master/data/",
              "imagenet_classes.txt",
          ]
      )
      class_name = "imagenet_classes.txt"
      class_path = download_testdata(class_url, class_name, module="data")
      with open(class_path) as f:
          class_id_to_key = f.readlines()
      
      class_id_to_key = [x.strip() for x in class_id_to_key]
      
      # Get top-1 result for TVM
      top1_tvm = np.argmax(tvm_output.asnumpy()[0])
      tvm_class_key = class_id_to_key[top1_tvm]
      
      # Convert input to PyTorch variable and get PyTorch result for comparison
      torch_t0 = time.clock()
      for i in range(10):
          with torch.no_grad():
              torch_img = torch.from_numpy(img)
              output = model(torch_img)
      
              # Get top-1 result for PyTorch
              top1_torch = np.argmax(output.numpy())
              torch_class_key = class_id_to_key[top1_torch]
      torch_t1 = time.clock()
      
      tvm_time = tvm_t1 - tvm_t0
      torch_time = torch_t1 - torch_t0
      
      print("Relay top-1 id: {}, class name: {}".format(top1_tvm, key_to_classname[tvm_class_key]))
      print("Torch top-1 id: {}, class name: {}".format(top1_torch, key_to_classname[torch_class_key]))
      print('Relay time: ', tvm_time / 10.0, 'seconds')
      print('Torch time: ', torch_time / 10.0, 'seconds')
      

      0x05. 小节

      这一节是对TVM的初步介绍,暂时讲到这里,后面的文章会继续深度了解和介绍深度学习编译器相关的知识。

      0x06. 参考

      • http://tvm.apache.org/docs/tutorials/frontend/from_pytorch.html#sphx-glr-tutorials-frontend-from-pytorch-py
      • https://zhuanlan.zhihu.com/p/50529704

      ————————————————————————
      转载来源:公众号【GiantPandaCV】
      欢迎关注GiantPandaCV, 在这里你将看到独家的深度学习分享,坚持原创,每天分享我们学习到的新鲜知识。( • ̀ω•́ )✧
      有对文章相关的问题,或者想要加入交流群,欢迎添加BBuf微信:

      posted in 机器学习
      Violet_恒源智享云
      Violet_恒源智享云
    • 【GiantPandaCV】人物属性模型移动端实验记录

      【GiantPandaCV导语】最近项目有需求,需要把人物属性用在移动端上,需要输出性别,颜值和年龄三个维度的标签, 用来做数据分析收集使用,对速度和精度有一定的需求,做了一些实验,记录如下。

      一、模型

      • SingleHead
      1. backbone+avgpool后面 接一个卷积,卷积核为(inp, (gender_class+beauty_class+age_class), 3, 3)
      2. backbone+avgpool后面 接入一个channel shuff层, 再接入一个卷积,和第一种一样。
      • MutilHead
      1. backbone+avgpool后面,接入三个FC,每个FC对应一个维度的任务。
      2. backbone+avgpool后面,先接入一个SE模块后,接三个FC,每个FC对应一个维度的任务。
      3. backbone+avgpool后面,接入一个512维度的FC,后接入三个FC,每个FC对应一个维度的任务。
      4. backbone+avgpool后面,接入三个512维度的FC来做embeeding,后接入三个FC,每个FC对应一个维度的任务。

      如下图所示:

      图1-不同模型结构

      训练: 训练数据总计35w,每张图片都带有三个维度的标签,使用Horovod分布式框架进行训练,采用SGD优化器,warmup5个epoch,使用cosine进行衰减学习率,总计训练60个epoch,训练代码可以参考https://github.com/FlyEgle/cub_baseline。

      实验对比:对于SingleHead模型,MutilHead的1,2模型,采用的是mobilenetv2作为backbone,对于MutilHead的3,4模型,采用的是mobilenetv2x0.5作为backbone。这里对比的baseline为resnest50的结果,结果如下:

      图2-结果对比

      结论:出于性能和速度的考虑,确定了以mbv2x0.5作为backbone,模型结构为mutilhead-4的模型。

      二、蒸馏

      mobilenetv2与resnest50在imagenet上的baseline大概相差8个点左右,所以我们自身的实验跑出来的结果也是在合理的范围内。为了进一步提升小模型的精度,选择用resnest50的模型来蒸馏mbv2x0.5的模型(ps:这里尝试过训练一个mbv2x2的模型,不过没有训的比resnest50高,所以还是使用resnest50)。蒸馏,采用的是传统的蒸馏方法,KL散度来作为损失,由于head相同,所以只需要考虑对logits蒸馏即可,KL散度代码如下:
      class KLSoftLoss(nn.Module):
      r"""Apply softtarget for kl loss

      Arguments:
          reduction (str): "batchmean" for the mean loss with the p(x)*(log(p(x)) - log(q(x)))
      """
      def __init__(self, temperature=1, reduction="batchmean"):
          super(KLSoftLoss, self).__init__()
          self.reduction = reduction
          self.eps = 1e-7
          self.temperature = temperature
          self.klloss = nn.KLDivLoss(reduction=self.reduction)
      
      def forward(self, s_logits, t_logits):
          s_prob = F.log_softmax(s_logits / self.temperature, 1)
          t_prob = F.softmax(t_logits / self.temperature, 1)
          loss = self.klloss(s_prob, t_prob) * self.temperature * self.temperature
          return loss
      

      训练:对于分类的问题,一般情况只是蒸馏输出的logits即可,由于多任务有多个head,所以会有多个logits,分别蒸馏即可,整体框架如下:

      图4-蒸馏训练框架

      蒸馏训练代码如下,由于学生和教师的网络差异性较大同时精度相差甚远,所以采用1:1的比例来进行训练,蒸馏的温度为25(T=5):

      图5-蒸馏训练代码

      结论:采用了3中不同的分辨率进行蒸馏实验,其中训练的size为224,推理为256的时候效果最好。

      三、剪枝

      Slimming Prune,实验采用的剪枝方法是来自于Learning Efficient Convolutional Networks through Network Slimming,通过对BN的channel进行稀疏化来达到剪枝的效果(个人喜欢用比较简单稳定的方法,便于debug和修改)。

      图6-Slimming

      训练和剪枝

      • 训练,训练代码很简单,只需要再更新权重之前进行稀疏化处理即可,sr是超参,一般设置为1e-4,代码如下:
      optimizer.zero_grad()
        loss.backward()
      
         # use the slimming prune for training
        if args.prune and args.use_sr:
            for m in model.modules():
                if isinstance(m, nn.BatchNorm2d):
                    m.weight.grad.data.add_(args.sr * torch.sign(m.weight.data))
      
        optimizer.step()
      
      • 剪枝, 由于模型结构是mobilenetv2的结构,有DW存在,所以,在剪枝的时候需要注意groups的数量和channel需要保持一致,同时,为了方便移动端优化加速,要保证channel是8的倍数,剪枝代码逻辑如下:
        先设置一定的剪枝比例p,如0.1,0.2,0.3…,按BN的channel总数从小到大来进行过滤。
        保留最大比例的最小阈值,防止prune过大,导致模型崩溃。
        对于不满足8的倍数的channel,按8的倍数补齐,补齐的方法是对prune过的channel排序,从大到小按差值补齐。
        保存除了第一个InvertedResidual模块以外的所有模块剪枝后的channel数量,重构模型。
        测试结果,考虑是否进行finetune训练。

      剪枝部分代码如下:

      def prune_only_res_hidden(percent, model, keep_channel=True, channel_ratio=8, cuda=True):
          """only prune the inverResidual module first bn layer
          """
          total = 0
          highest_thre = []
          for m in model.modules():
              if isinstance(m, InvertedResidual):
                  # only prune the 3 conv layer
                  if len(m.conv) > 5:
                      for i in range(len(m.conv)):
                          if i == 1:
                              if isinstance(m.conv[i], nn.BatchNorm2d):
                                  total += m.conv[i].weight.data.shape[0]
                                  highest_thre.append(m.conv[i].weight.data.abs().max().item())
                                  total += m.conv[i+3].weight.data.shape[0]
                                  highest_thre.append(m.conv[i+3].weight.data.abs().max().item())
      
      
      
          bn = torch.zeros(total)
          index = 0
          for m in model.modules():
              if isinstance(m, InvertedResidual):
                  # only prune the 3 conv layer
                  if len(m.conv) > 5:
                      for i in range(len(m.conv)):
                          if i != len(m.conv) - 1:
                              if isinstance(m.conv[i], nn.BatchNorm2d):
                                  size = m.conv[i].weight.data.shape[0]
                                  bn[index:(index+size)] = m.conv[i].weight.data.abs().clone()
                                  index += size
      
          print(bn.size())
          y, i = torch.sort(bn)
          thre_index = int(total * percent)
          thre = y[thre_index]
          highest_thre = min(highest_thre)
      
          # 判断阈值
          if thre > highest_thre:
              thre = highest_thre
      
          print("the min thre is {}, the max thre is {}!!!!".format(thre, highest_thre))
          pruned = 0
          c = {}
          cfg_mask = []
          idx = 0
      
          for m in model.modules():
              if isinstance(m, InvertedResidual):
                  # only prune the 3 conv layer
                  if len(m.conv) > 5:
                      for i in range(len(m.conv)):
                          if i == 1:
                              if isinstance(m.conv[i], nn.BatchNorm2d):
                                  weight_copy = m.conv[i].weight.data.clone()
                                  if cuda:
                                      mask = weight_copy.abs().gt(thre).float().cuda()
                                  else:
                                      mask = weight_copy.abs().gt(thre).float()
      
                                  if keep_channel:
                                      keep_channel_number = get_min_number(torch.sum(mask), channel_ratio)
                                      if torch.sum(mask) < keep_channel_number:
                                          n = int(keep_channel_number - torch.sum(mask))
                                          mask_index = torch.where(mask==0)[0]
                                          new_weight = weight_copy.abs()[mask_index]
                                          _, weight_index = torch.sort(new_weight)
                                          w_index = mask_index[weight_index[-n: ]]
                                          mask[w_index] = 1.0
      
                                  pruned = pruned + mask.shape[0] - torch.sum(mask)
                                  # first conv + bn
                                  m.conv[i].weight.data.mul_(mask)
                                  m.conv[i].bias.data.mul_(mask)
                                  # second conv + bn
                                  m.conv[i+3].weight.data.mul_(mask)
                                  m.conv[i+3].bias.data.mul_(mask)
                                  c[idx] = int(torch.sum(mask))
                                  cfg_mask.append(mask.clone())
      
                                  print('layer index: {:d} \t total channel: {:d} \t remaining channel: {:d}'.format(idx, mask.shape[0], int(torch.sum(mask))))
                                  idx += 1
          print(c)
          print(len(c))
          print(len(cfg_mask))
          # pruned_ratio = pruned / total
          print('Pre-processing Successful!!!')
          return model, cfg_mask, c
      

      直接保存模型后测试,对比结果如下:

      再次使用resnest50进行蒸馏后,对比结果如下:

      添加2w标注的业务数据,总计训练数据37w,蒸馏后的结果如下:

      针对性能的需求,考虑用0.3的版本,如果速度要求更快的话,考虑0.4的版本。

      四、TODO

      1. 训练一个基于BYOL的pretrain模型。
      2. 把没有标注的数据,用模型打上伪标签后参与训练。
      3. 训练一个更大的teacher模型。
      4. 使用百度的JSDivLoss作为蒸馏损失。

      五、结论

      • 对于移动端的任务来说,蒸馏和剪枝是必不可少的,尤其是要去训练一个比较好的teacher,这里的teacher可以同结构也可以异结构,只要最后logits一致即可。
      • 由于移动端会根据X8或者X4的倍数优化,所以剪枝的时候尽量保持channel的倍数,建议常备一种便于修改的剪枝代码。
      • 小模型具备成长为大模型的潜质,只要训练方法适当。

      结束语

      本人才疏学浅,以上都是自己在做项目中的一些方法和实验,以及一些粗浅的思考,并不一定完全正确,只是个人的理解,欢迎大家指正,留言评论。

      参考文献

      • mobilenetv2 https://export.arxiv.org/pdf/1801.04381

      • resnest https://export.arxiv.org/pdf/2004.08955

      • Slimming prune https://arxiv.org/pdf/1708.06519.pdf

      ————————————————————————————
      转载来源:公众号【GiantPandaCV】
      欢迎关注GiantPandaCV, 在这里你将看到独家的深度学习分享,坚持原创,每天分享我们学习到的新鲜知识。( • ̀ω•́ )✧
      有对文章相关的问题,或者想要加入交流群,欢迎添加BBuf微信:

      posted in CV领域
      Violet_恒源智享云
      Violet_恒源智享云
    • 【TechFlow】机器学习基础——从线性回归到逻辑回归【硬核推导,附代码】

      在之前的文章当中,我们推导了线性回归的公式,线性回归本质是线性函数,模型的原理不难,核心是求解模型参数的过程。通过对线性回归的推导和学习,我们基本上了解了机器学习模型学习的过程,这是机器学习的精髓,要比单个模型的原理重要得多。

      新关注和有所遗忘的同学可以点击下方的链接回顾一下之前的线性回归和梯度下降的内容。

      机器学习基础——梯度下降法

      机器学习基础——推导线性回归公式

      回归与分类

      在机器学习当中,模型根据预测结果的不同分为两类,如果我们希望模型预测一个或者多个连续值,这类问题被称为是回归问题。像是常见的未来股票价格的估计、未来温度估计等等都算是回归问题。还有一类呢是分类问题,模型的预测结果是一个离散值,也就是说只有固定的种类的结果。常见的有垃圾邮件识别、网页图片鉴黄等等。

      我们之前介绍的逻辑回归顾名思义是一个回归问题,今天的文章讲的呢是如何将这个回归模型转化成分类模型,这个由线性回归推导得到的分类模型称为逻辑回归。

      逻辑回归

      逻辑回归这个模型很神奇,虽然它的本质也是回归,但是它是一个分类模型,并且它的名字当中又包含”回归“两个字,未免让人觉得莫名其妙。

      如果是初学者,觉得头晕是正常的,没关系,让我们一点点捋清楚。

      让我们先回到线性回归,我们都知道,线性回归当中y=WX+b。我们通过W和b可以求出X对应的y,这里的y是一个连续值,是回归模型对吧。但如果我们希望这个模型来做分类呢,应该怎么办?很简单,我们需要人为地设置阈值对吧,比如我们规定y > 0最后的分类是1,y < 0最后的分类是0。从表面上来看,这当然是可以的,但实际上这样操作会有很多问题。

      最大的问题在于如果我们简单地设计一个阈值来做判断,那么会导致最后的y是一个分段函数,而分段函数不连续,使得我们没有办法对它求梯度,为了解决这个问题,我们得找到一个平滑的函数使得既可以用来做分类,又可以解决梯度的问题。

      很快,信息学家们找到了这样一个函数,它就是Sigmoid函数,它的表达式是:

      它的函数图像如下:

      可以看到,sigmoid函数在x=0处取值0.5,在正无穷处极限是1,在负无穷处极限是0,并且函数连续,处处可导。sigmoid的函数值的取值范围是0-1,非常适合用来反映一个事物发生的概率。我们认为σ(x)表示x发生的概率,那么x不发生的概率就是1-σ(x)。我们把发生和不发生看成是两个类别,那么sigmoid函数就转化成了分类函数,如果σ(x)>0.5表示类别1,否则表示类别0.

      到这里就很简单了,通过线性回归我们可以得到y=WX+b,P(1)=σ(y),P(0)=1-σ(y)。也就是说我们在线性回归模型的外面套了一层sigmoid函数,我们通过计算出不同的y,从而获得不同的概率,最后得到不同的分类结果。

      损失函数

      下面的推导全程高能,我相信你们看完会三连的(在看、转发、关注)。
      让我们开始吧,我们先来确定一下符号,为了区分,我们把训练样本当中的真实分类命名为y,y的矩阵写成Y。同样,单条样本写成x, x的矩阵写成X。单条预测的结果写成y^ (y hat),所有的预测结果的矩阵写成Y^ (Y hat)。

      对于单条样本来说,y有两个取值,可能是1,也可能是0,1和0代表两个不同的分类。我们希望y=1的时候,y^尽量大,y=0时,1-y^尽量大,也就是y^尽量小,因为它取值在0-1之间。我们用一个式子来统一这两种情况:

      我们代入一下,y=0时前项为1,表达式就只剩下后项,同理,y=0时,后项为1,只剩下前项。所以这个式子就可以表示预测准确的概率,我们希望这个概率尽量大。显然,P(y丨x)>0,所以我们可以对它求对数,因为log函数是单调的。所以P(y丨x)取最值时的取值,就是log P(y丨x)取最值的取值。

      我们期望这个值最大,也就是期望它的相反数最小,我们令,这样就得到了它的损失函数:

      如果知道交叉熵这个概念的同学,会发现这个损失函数的表达式其实就是交叉熵。交叉熵是用来衡量两个概率分布之间的“距离”,交叉熵越小说明两个概率分布越接近,所以经常被用来当做分类模型的损失函数。关于交叉熵的概念我们这里不多赘述,会在之后文章当中详细介绍。我们随手推导的损失函数刚好就是交叉熵,这并不是巧合,其实底层是有一套信息论的数学逻辑支撑的,我们不多做延伸,感兴趣的同学可以了解一下。

      硬核推导

      损失函数有了,接下来就是求梯度来实现梯度下降了。

      这个函数看起来非常复杂,要对它直接求偏导过于硬核(危),如果是许久不碰高数的同学直接肝不亚于硬抗苇名一心。

      为了简化难度,我们先来做一些准备工作。首先,我们先来看下σ函数,它本身的形式很复杂,我们先把它的导数搞定。

      因为y^=σ(θX),我们将它带入损失函数,可以得到:

      接着我们求对J(θ)的θ偏导,这里要代入上面对*σ(x)*求导的结论:

      代码实战

      梯度的公式都推出来了,离写代码实现还远吗?

      不过巧妇难为无米之炊,在我们撸模型之前,我们先试着造一批数据。

      我们选择生活中一个很简单的场景——考试。假设每个学生需要参加两门考试,两门考试的成绩相加得到最终成绩,我们有一批学生是否合格的数据。希望设计一个逻辑回归模型,帮助我们直接计算学生是否合格。

      为了防止sigmoid函数产生偏差,我们把每门课的成绩缩放到(0, 1)的区间内。两门课成绩相加超过140分就认为总体及格。

      import numpy as np
      def load_data():
          X1 = np.random.rand(50, 1)/2 + 0.5
          X2 = np.random.rand(50, 1)/2 + 0.5
          y = X1 + X2
          Y = y > 1.4
          Y = Y + 0
          x = np.c_[np.ones(len(Y)), X1, X2]
          return x, Y
      

      这样得到的训练数据有两个特征,分别是学生两门课的成绩,还有一个偏移量1,用来记录常数的偏移量。

      接着,根据上文当中的公式,我们不难(真的不难)实现sigmoid以及梯度下降的函数。

      def sigmoid(x):
          return 1.0/(1+np.exp(-x))
          
      def gradAscent(data, classLabels):
          X = np.mat(data)
          Y = np.mat(classLabels)
          m, n = np.shape(dataMat)
          # 学习率
          alpha = 0.01
          iterations = 10000
          wei = np.ones((n, 1))
          for i in range(iterations):
              y_hat = sigmoid(X * wei)
              error = Y - y_hat
              wei = wei + alpha * X.transpose()*error
          return wei
      

      这段函数实现的是批量梯度下降,对Numpy熟悉的同学可以看得出来,这就是在直接套公式。

      最后,我们把数据集以及逻辑回归的分割线绘制出来。

      import matplotlib.pyplot as plt
      def plotPic():
          dataMat, labelMat = load_data()
          #获取梯度
          weis = gradAscent(dataMat, labelMat).getA()
          n = np.shape(dataMat)[0]
          xc1 = []
          yc1 = []
          xc2 = []
          yc2 = []
          #数据按照正负类别划分
          for i in range(n):
              if int(labelMat[i]) == 1:
                  xc1.append(dataMat[i][1])
                  yc1.append(dataMat[i][2])
              else:
                  xc2.append(dataMat[i][1])
                  yc2.append(dataMat[i][2])
          fig = plt.figure()
          ax = fig.add_subplot(111)
          ax.scatter(xc1, yc1, s=30, c='red', marker='s')
          ax.scatter(xc2, yc2, s=30, c='green')
          #绘制分割线
          x = np.arange(0.5, 1, 0.1)
          y = (-weis[0]- weis[1]*x)/weis[2]
          ax.plot(x, y)
          plt.xlabel('X1')
          plt.ylabel('X2')
          plt.show()
      

      最后得到的结果如下:

      随机梯度下降版本

      可以发现,经过了1万次的迭代,我们得到的模型已经可以正确识别所有的样本了。

      我们刚刚实现的是全量梯度下降算法,我们还可以利用随机梯度下降来进行优化。优化也非常简单,我们计算梯度的时候不再是针对全量的数据,而是从数据集中选择一条进行梯度计算。

      基本上可以复用梯度下降的代码,只需要对样本选取的部分加入优化。

      def stocGradAscent(data, classLab, iter_cnt):
          X = np.mat(data)
          Y = np.mat(classLab)
          m, n = np.shape(dataMat)
          weis = np.ones((n, 1))
          for j in range(iter_cnt):
              for i in range(m):
                  # 使用反比例函数优化学习率
                  alpha = 4 / (1.0 + j + i) + 0.01
                  randIdx = int(np.random.uniform(0, m))
                  y_hat = sigmoid(np.sum(X[randIdx] * weis))
                  error = Y[randIdx] - y_hat
                  weis = weis + alpha * X[randIdx].transpose()*error
          return weis
      

      我们设置迭代次数为2000,最后得到的分隔图像结果如下:

      当然上面的代码并不完美,只是一个简单的demo,还有很多改进和优化的空间。只是作为一个例子,让大家直观感受一下:其实自己亲手写模型并不难,公式的推导也很有意思。这也是为什么我会设置高数专题的原因。CS的很多知识也是相通的,在学习的过程当中灵感迸发旁征博引真的是非常有乐趣的事情,希望大家也都能找到自己的乐趣。

      ————————————————————————
      转载来源:公众号【TechFlow】

      posted in 机器学习
      Violet_恒源智享云
      Violet_恒源智享云
    • 【使用技巧】实例计费类型随时切换

      在使用过程中,可能会想更换计费类型,比如:

      1. 按量付费的实例切换成包周包月包年的实例;
      2. 包周包月包年切换成按量付费实例。
        下面我们就分别介绍一下这两种情况怎么操作。

      I. 按量付费的实例 ➡️ 包周包月包年的实例

      1. 对处于“运行中”状态的按量付费实例,点击“实例管理”-“转包周包月包年”

      2. 选择新的计费模式,可以看到对应计费模式的价格,以及最终支付的金额

      3. 点击“确定变更”,即可变更成功

      注意:仅部分机型支持包周包月包年。

      II. 包周包月包年 ➡️ 按量付费实例。

      1. 在实例列表中,点击付费方式列的“变更”
        6372c9e4-83e1-4870-a5d8-76a83f5066f2.png

      2. 在“到期管理”弹窗中,选择实例到期后希望转换的计费模式,及相应的价格。
        可以选择到期后:自动续费/ 关机/转按量付费

      3. 点击确定,设置完成。设置后的效果可以在列表中查看。

      posted in 新手教程
      Violet_恒源智享云
      Violet_恒源智享云
    • 【TechFlow】知乎 | 一年半的时间成为算法工程师,这可能吗?

      最近在知乎里面看到了一个很有意思的问题,提问的同学是这样说的:

      今年研一,感觉自己什么也不会,导师完全放养,2个月没见过面,从入学到现在一个学期快结束了总共见了5次面,没有项目,没有指导,哎 想做机器学习方面的工作,距离明年秋招还有一年半,目前在看C++primer,然后学一学数据结构和算法,同时再学校李航的《统计学习方法》,但是感觉很吃力;另外面试肯定需要介绍项目经历,这方面又太欠缺,不知如何是好,辗转反侧,彻夜难眠。求助各位大佬,我该如何准备,才能拿到一个算法offer?

      感觉自己什么也不会,导师放养,又想要拿一个offer,但时间有限,只有一年半。我想,很多同学在硕士阶段可能都有这样的困惑。本来这是一个很好的问题,但让我遗憾的是,高票回答聊的都是项目经验、竞赛、简历、LeetCode这些。感觉没有一个达到点上的,所以今天和大家聊聊这个问题,希望可以对迷茫当中的同学们有点帮助。

      一

      为什么说上来就回答应该刷项目经验、LeetCode的回答不在点上呢,先不说这些举措究竟有多大的用处,单说这样的答案本身就是不对的。因为这只是知其然而没有知其所以然,我们知道要刷LeetCode,但是不知道究竟为什么要刷,它究竟有什么用。我们知道要刷项目经历,但不知道要刷什么样的项目,又要刷到什么样的程度。这些建议看似非常明确,但是背后有着非常巨大的未知,贸贸然去做,反而会陷入迷茫。

      如果我们把一年半拿到offer看成是目标的话,我们首先要做的不是立即行动,而是先对目标进行背景调查、搜集信息,对它积累足够的了解。之后再来分析目标,拆解它的必要条件。最后再从这些必要条件出发,把满足这些必要条件当做是目标,再来进行同样的分析。

      我不知道这位同学为什么想要做算法,不知道他出发的初心。但是当我们做出职业方向的选择的时候,我们是需要一个强有力的心理支撑的。就是你究竟为什么想做算法?你觉得算法工程师都是干嘛的?它在互联网公司当中起到的作用是什么?

      只有这三个问题想清楚了,才不会陷入迷茫,也不会将来入行了之后发现和自己当初想的不一样而后悔。

      二

      那算法工程师究竟是干嘛的呢?

      很简单,这行的作用就是推动业务的发展。调参、训练模型、复现paper……这些只是手段,不是目的。核心的目的还是业务的增长,提升业务指标。其实也不只是算法,几乎所有技术岗位的本职都是这个。所以为什么我之前会说技术驱动的公司只是一个口号,实际上几乎所有公司都是业务驱动的。

      问题来了,既然使命是推动业务增长,那么业务是什么?

      电商的搜索、推荐、广告算不算业务?自动驾驶算不算业务?机器翻译、语音识别是不是业务?如果让我回答,第一个肯定是业务,第二个勉强算,第三个更偏技术。当今算法领域几大方向,推荐、CV、NLP,你会发现只有推荐是和业务强挂钩的。CV和NLP都不直接关联业务,比如CV做图像识别,图像识别只是技术,我们把图像识别用在安保上,安保才是业务,图像识别本身不算。NLP也是一样,单纯的NLP技术不算是业务,大厂当中往往把NLP用在提升搜索、推荐的效果上,搜索、推荐是业务,NLP不是。

      这就是我为什么推荐大家选择推荐的原因,除了技术本身门槛相对较低之外,另外一个很重要的点是它距离业务很近。想要入行,最起码得知道,你想要做哪一块业务,在这一块业务当中算法是如何起作用的,又涉及哪些知识和技能。到这里基本上没涉及技术,你可以理解成对这个行业的一个背景调查。

      搜索推荐不谈了,拿CV举个例子,如果是电商领域,CV大概会有这么几个作用。一个是以图搜图,也就是基于图片的搜索;一个是鉴黄、风控,对违规的商品、图片进行识别;一个是图片分析提取特征,比如说商品的封面图的质量,对用户的吸引力等等。然后再是针对这些场景需要用到的一些技术,比如CNN、YOLO等等。

      业务方向选好了,那么对应的技术栈也就清楚了。比如说你选了推荐,那么你剩下的就是了解当今的推荐系统是如何运作的,它当中设计那些技术,算法的作用是什么,用到哪些模型,哪些技术,又有那些前沿的paper。你顺着这条线一路摸下来,按图索骥,网上大神的博客读一读,各类专栏看一看,走马观花,了解一个大概,基本上就可以做到心中有数了。

      三

      当你对于岗位的业务以及对应的技术栈都有一个大概的认知之后,就可以开始动手学习了。

      学习的顺序也比较明确,首先是机器学习基础,再是深度学习基础,再是对应领域的一些前沿技术和paper。这是一个万金油的路径,不管你想要从事什么领域的算法,都可以沿着这个路径出发。这里我有一个建议是机器学习相关的模型浅尝辄止,知道原理和推导过程就可以了,不需要细扣。一个是因为这些模型基本上淘汰用不到了,另外一个反正之后准备面试的时候也是需要复习的,再加上一些模型的细节很容易忘,比如SVM的推导过程什么的,所以前期学习的时候可以适当宽松一些。

      根据我个人的经验,机器学习基础再加上深度学习的基础这两个加在一起三个月绰绰有余。业务相关的技术和论文篇幅也并不大,以推荐领域举例,如果只是学习技术,不涉及实战的话,我个人感觉三个月也绰绰有余。这两项加在一起也不过用了半年的时间。只有再用半年的时间去刷一些实战经验,比如kaggle当中的比赛,比如腾讯、阿里举办的各种比赛等等。

      最后剩下的半年才是刷LeetCode和算法题,复习巩固一些机器学习的基础(SVM、随机森林、决策树)这些,准备实习面试。真能拿出半年的时间来准备,每天花两个小时刷5题,LeetCode前300题刷上个三遍问题不大。剩下的时间海投简历,看看面经,复习一下学过的基础。不说板上钉钉,至少找到一个还不错的实习机会问题不是很大。

      只要能拿到实习的机会,基本上就算是入行了,后面的校招路会好走很多。就算一下子进不了大厂也没有关系,先进个中小型企业打磨技术和能力也未尝不可。

      尾声

      简单总结一下,学技术是为了应用在业务当中,算法尤其如此。所以首先应该找到从业的方向,找到自己想要从事的领域。有了方向之后再根据对应的要求来进行学习,等学得差不多了,也有了一定的实战经验之后,再是刷题准备面试。我自己当初转行的时候只用了几个月,一年半的时间真的完全够了。路走对了,只要能坚持下来,成功率绝对不低。

      千万不要盲目,上来就刷LeetCode,就啃一些模型,搞了半天也不知道这些东西学来干嘛的,这就本末倒置了。

      好了,今天的文章就到这里,感谢阅读,喜欢的话不要忘了三连。

      ————————————————————————
      转载来源:公众号【TechFlow】

      posted in 聊一会吧
      Violet_恒源智享云
      Violet_恒源智享云
    • 【TechFlow】小白都能懂的推荐算法入门(一),从上古时期的LR开始聊起

      大家好,不知不觉做推荐系统挺久的了,很多同学私信让我好好写写推荐系统相关,但苦于沉淀不够有货倒不出。最近充电总结了一段时间,有了一些积累,给大家好好说道说道。

      既然是介绍推荐系统,我们先从最简单最简单的模型开始说起。说起推荐算法领域最简单的模型,自然就是LR了。

      LR模型是机器学习领域的基础模型,我想接触过这个领域的同学应该都不陌生。如果对LR模型的原理有所好奇的话,可以点击下方传送门回顾一下。

      原创 | 万字长文,剖析经典论文GBDT+LR

      为什么是LR

      什么是LR大家已经知道了,但还有一个问题却没有回答。那就是为什么早年的时候LR模型如此受欢迎呢?难道就不能使用其他一些看起来高级一些的模型吗,比如决策树、随机森林、GBDT?不是说XGBoost在各种比赛的效果都非常好吗?为什么业内不用来做推荐呢?

      尤其是当我读到2014年Facebook推出的GBDT+LR的paper的时候,这种困惑更是明显。

      这篇论文非常经典,在业内地位很重,甚至可以说是推荐领域必读的paper之一。深度学习兴起之前很多公司和厂商都沿用了这个做法,论文当中的做法倒是不难,说是创新的做法,其实本质上就是将GBDT预测的时候样本落到的节点作为multi-hot编码,然后将这个编码之后的01的数组看成是新的特征,然后用这个转换过的特征来训练LR。可以说它的本质仍然是训练LR,所谓的GBDT只不过是一个编码器。

      我之前写过这篇paper的一个完整的解析,如果大家对这篇paper当中的内容感兴趣,也可以访问传送门进行跳转。

      机器学习基础——从线性回归到逻辑回归【硬核推导,附代码】

      我当时看这篇paper的时候,里面的意思都已经理解了,但是有一个问题怎么也没想明白。既然都用GBDT了,结合其他模型不香吗,非得结合LR?

      我估计这个问题很多在推荐领域的从业者可能也未必答得上来,我先卖个关子,把问题记在这里,等会晚点来回答。

      推荐领域的特征有什么特点?

      在算法领域,提及效果,特征和模型两者是一体两面,很难剥离。好的模型也需要好的特征支撑,好的特征需要好的模型才能充分表达。所以我们先把模型的问题放一放,来思考一下特征。

      推荐领域主要的特征只有三块,以电商为例,分别是item,user和context。也就是商品,用户以及环境信息,比如时间,地点,展示位置等等。context特征比较少,来来回回就那么几样,我们也先放一放。剩下的就是用户和商品,围绕用户和商品我们形成的特征主要又可以分成两个部分,一个是基础特征,另外一个是统计特征。

      以商品举例,基础特征就是品牌、价格、类目、评价,统计特征就是最近点击率、最近销售额、最近转化率等等。这些特征按照类别分又可以分为两种,一种是浮点型的连续型特征,一种是类别特征,比如商品的类目,品牌等等。到这里都很正常,没有什么难理解,或者是不可思议的部分。

      我们接着往下,再来看看模型要预测的目标——点击率。我们结合一下模型预测的目标再来观察一下前面列举的特征,你会发现,除了历史点击率、历史转化率等少数几个指标和最终的结果是强正相关之外,其他的浮点型的特征没有特别明显的正相关或者是负相关。可以说商品的价格和点击率负相关吗?其实不太行,商品越便宜可能质量越差,反而不会有人点。用户的购买力呢?越有钱点的商品越多吗?也不成立。

      正是因为上面说的这个原因,所以在推荐领域,效果很好的浮点型特征很少,大部分都是类别特征,也就是01特征。

      所以你说GBDT、随机森林、XGboost这些模型的效果会很好吗?很难说,因为这些模型的长处往往都在浮点型特征,也就是连续型特征。这些树模型会设计规则对这些连续特征进行分段,如果大部分特征都是01特征,那还怎么分段呢?

      所以,到这里也就回答了,为什么在深度学习模型兴起之前,推荐领域普遍都使用LR,而不是那些看着很牛的树模型。

      LR模型的原理

      LR模型也就是纯线性模型,它可以简单理解成若干个特征的加权和。每个特征的权重或大或小,最后累加在一起,得到一个预测的概率。这毫无毛病,也是学过的人都知道。

      但我们往下一层,有没有想过这一点在推荐领域意味着什么呢?

      意味着模型其实是”记住“了每个特征和最终结果的关系,我们把模型拟人化,把它看成一个机器人的话。机器人看到样本有特征A并且点击了,于是特征A的权重提升一点,样本有特征B但是没点击,于是把特征B的权重降低一些。模型就是在这样一个策略当中找到一个最佳的平衡。

      这就意味着,一些容易被记忆的特征往往会发挥比较好的效果。比如男士通常会买烟,女士通常买口红,那么我们就可以设计男士_烟和女士_口红的组合特征。当模型看到大部分男士看到烟都点击了之后,它就能学到这个组合是一个强特征并给与一个比较高的权重。这样只要我们尽可能地找出这些特征的组合,那么模型就可以得到很好的效果。

      所以到这里大家就明白了,LR模型在推荐领域发挥作用,本质上就是靠的“记性”。因为它可以记住那些类别特征以及类别特征的组合,所以它往往比那些看起来更高端的树模型效果要好。这也是为什么到了LR时代的后期,算法工程师们的工作就是整天挖掘一些类别特征的组合,以期望模型达到很好的效果。

      LR模型的优缺点

      到这里,关于LR模型在推荐领域的应用就差不多说完了,我们做一个简单的总结,首先从它的优点开始说起。

      LR模型的优点教科书上已经说了很多了,比如训练速度快,由于参数空间比较小,LR模型可以迅速收敛,它的训练速度要比那些树模型以及后面的深度学习模型快得多。其次是可解释性强,由于我们可以查阅得到所有特征的权重,所以我们很容易解释究竟是什么特征发挥了作用,或者是什么特征拖了后腿。

      但是LR在推荐领域也有一个很大的缺点,是什么呢,就是脏活累活很多。

      因为几乎所有的特征组合都需要人工挖取,需要人工遍历很多特征组合,甚至是一一尝试找到最佳的组合。这个过程当中需要花费大量的人力,几乎可以说是纯堆人工。所以对于LR时代的算法工程师来说可能螺丝钉的感觉比现在还要严重得多,什么优化模型基本上是不用想了,LR这么简单的模型也没什么优化的空间,剩下的事情基本上就只有做特征做实验了。

      俗话说得好,前辈的缺点就是后辈努力的方向。那么对于LR模型的这些缺点又该怎么弥补或者是优化呢?

      容我卖个关子,欲知后事如何,且听下回分解。

      ————————————————————————
      转载来源:公众号【TechFlow】

      posted in 机器学习
      Violet_恒源智享云
      Violet_恒源智享云
    • RE: 【有奖话题NO.7】传说中的万能Transformer,你有用过吗?

      @155-7220 大佬真厉害

      posted in 有奖话题
      Violet_恒源智享云
      Violet_恒源智享云
    • 【号外】听说一个好消息……福利要升级!很快了!

      555ff591-88b9-43e0-a3dc-da9948a71d56-wecom-temp-a52ce2071077593ba209a59df046456c.jpg

      这几天,Violet有点憋坏了😫
      事情是这样的
      前几天路过老板办公室,他好像在说学生福利要升级???😯
      好想立刻告诉你们,但是我怕被老板骂😭
      好煎熬😭

      然后,我想
      人生苦短,不吐不快!
      先斩后奏,他发现也来不及了,嘿嘿😛

      所以我知道的就是:
      学生福利要升级了!
      现在是95折,以后嘛……👍
      现在学生认证通过后领一次性代金券,以后嘛……🧧
      还有……😲

      嘿嘿,暂时就说这么多啦~~
      不怕,其实我也没说什么嘛,对吧!

      BTW
      还没认证的学生党们注意!!!
      👉
      8f5061d2-e372-4f53-97bb-48f0e5862623-wecom-temp-15f7006904acfef48624d54bdf9653ca.png

      答案很快就会揭晓啦!!🎉

      posted in 福利活动
      Violet_恒源智享云
      Violet_恒源智享云
    • 【TechFlow】买特斯拉可以,为什么不能完全相信自动驾驶?

      大家好,前几天的时候写过一篇滴滴和自动驾驶的文章,大家反响很好。有些小伙伴在后台给我留言说让我详细讲讲自动驾驶,倒不是我不想讲,而是确实没干过。好在我虽然没吃过猪肉,但之前听过很多大佬的内部分享,也算是看过猪跑了,就把我了解到的结合一些我自己的思考分享给大家。

      不过首先声明,这只是我个人了解消化到的信息,可能和实际情况有所出入。有所谬误是正常现象,大家去芜存菁,选择性地吸收即可。

      什么是自动驾驶?

      说到自动驾驶,不明就里的人以为就是AI或者是电脑自己开车,人往里面一坐什么都不用管就完了。这也不能说不对, 只能说是我们的终极追求。显然我们距离这个追求还有很长很长的一段路要走,所以为了让这个目标看起来不是那么遥不可及,人类发挥想象力和归纳能力,对自动驾驶技术进行了分级,将它分成了几个阶段,让这件事情看起来不再是那么不靠谱。

      目前比较公认的标准是国际自动机工程师学会,英文全称是Society of Automotive Engineers,简称SAE。SAE将自动驾驶分为从L0到L5一共六个等级,不得不说SAE是老法师,这个分级分得实在是太有水平了。我们一起来看下,原文是英文比较晦涩,我做了人话处理:

      • L0:无自动化,所有一切皆人力。
      • L1:驾驶支援,对驾驶员的驾驶有所辅助
      • L2:部分自动化,算法可以短暂接管车辆,驾驶员的眼睛和手可以短暂休息
      • L3:有条件自动化,车辆可以独立完成驾驶,但需要驾驶员盯着
      • L4:高度自动化,真正意义上自动驾驶,驾驶员不一定要盯着
      • L5:完全自动化,驾驶员都不需要了

      大家拿着这个标准对比一下特斯拉的所谓的自动驾驶的功能就能很明显地知道,马斯克是个大忽悠,特斯拉并不能自动驾驶。它顶多顶多只能做到L2,也就是在一些场景当中可以让驾驶员稍微休息一下。比如说高速上,大家可以松手,让车辆自动行驶一阵子。所以后来特斯拉不再强调自动驾驶这个概念了,更多地说是“辅助驾驶”。

      现在发展到什么阶段了?

      看完了国际标准,接下来我们再来看看发展的现状。大家肯定很好奇,现阶段究竟到什么程度了?

      对于这个问题我搜了很多资料,也没有看到一个定论。后来我找到了中国信通院于2021年2月发表的一篇叫做《各国自动驾驶政策概况及特征》的文章,在文章当中我找到了特别精髓的一段,就是下面这段,关键词我已经给高亮标出来了。

      看到了吗,2020年左右实现高速公路L3级别自动驾驶。看到L3先别激动,先看看它前面有一个非常非常内涵的限定词就是“高速公路”。我个人觉得这其实基本上就等于L2水平的自动驾驶的意思,为什么我这么说呢,因为高速公路这个场景下会避免很多很多现实的问题。

      我举个简单的例子,大家如果开过车上过高速就会知道,高速公路的路线是非常固定的,并且高速公路的路况要比城市乡村道路要简单得多。你在开高速的时候不会遇到红绿灯,也不会有行人,不会有乱窜的电瓶车,更不会有家禽、路边摊等复杂的情况。如果你是工程师,你也很难想象汽车在农村、山路、沙漠等极端路况以及环境当中会遇到什么诡异的case。所以在L3前面加上高速公路的限定词也是没有办法的事情,因为实际环境实在是太复杂了。所以根据我的估计,对于实验室以及一些理想路况的情况,目前的顶尖技术是可以达到L3甚至是L4,但对于放之四海而皆准的普通路况来说,保守估计应该只能实现L2。

      很多人可能会反驳我说,不对啊,百度的无人驾驶出租车不是在北京上线运行了吗,那也不是高速啊?

      但其实你仔细想想会发现北京这样的大城市同样满足理想路况的标准,虽然相比高速多了红绿灯,但一个是出租车一般来说的活动范围相对来说还是比较小的,基本上只包含市区,并且北京这样的大都市,路况也是相对有保障的,不可控因素较少。所以在这种相对理想的环境当中实现L3甚至是L4的可行性还是比较高的。

      听起来L3好像已经很厉害了,但实际上普通的家用车就是L1的水平,因为现在的家用车都有先进的车控系统,有电子助力器,有定速巡航等功能,妥妥的L1。所以最近几年一直在喊自动驾驶,但实际上只往前走了一步,可以理解成才刚出新手村不久,距离真正的boss还远着呢。

      自动驾驶目前的解决方案

      说到自动驾驶,很多人首先想到的还是特斯拉,第二想到的是各种深度学习的模型和技术,还有什么强化学习之类的。

      但实际上,汽车本身就是一个非常复杂的工程系统,在这样一个复杂的系统上做自动驾驶,相当于两个非常复杂的系统的叠加,显然这也是非常非常复杂的。我这么说大家可能感觉不到,所以下面我将会结合现在主流的自动驾驶的解决方案,来聊聊现在的方案以及对应的挑战。

      特斯拉自动驾驶方案

      目前市面上存在两套自动驾驶方案,分为主流的雷达方案以及特斯拉使用的图像识别方案。

      我估计大家可能对于特斯拉的方案更加感兴趣,所以先来说说特斯拉的方案。我在网上找到了这张概念图,大家英文不好没有关系,只需要知道图上标记的这些位置都是各种用途的摄像头就可以了。

      特斯拉采用的是V1R方案,即3个摄像头加上一个毫米波雷达,拥有3个前向摄像头以及一个77G毫米波雷达的传感器。下面这张图展示了车辆周身外能够感应到的距离,稍微有些模糊,我简单阐述一下,差不多就是往前最大可以感知到160米,左右两侧最大可以感知到60米,往后最大可以感知到100米。

      既然这么多摄像头和传感器,那应该是万无一失才对,为什么市面上特斯拉这么多因为自动驾驶的事故?甚至很多例子是面前一个巨大的障碍,但是车辆居然不知道闪避,就直直地撞上去了。

      这是因为特斯拉采用的是纯图像识别加上雷达感知的方案,基本上可以理解为以图像识别为主。图像识别有一个很大的问题是它的准确率相对并不高,即使在训练的过程当中可以达到99%以上的准确度,但是谁也不能保证它训练的样本当中已经覆盖了所有的情况。毕竟每个国家,每条路都可能有自己的特点,比如之前出过的一些事故,什么白色的卡车躺在路中间,结果模型给识别成了天上的白云,这样的例子屡见不鲜。

      这并不是图像识别的模型不行,而是模型没有办法覆盖到所有的情况,实际上工程师也不可能预料到公路上可能出现的所有情况,既然如此我们就不能保证系统一定一定是安全的。

      你可能还会奇怪,在上面的例子当中难道雷达就没有工作吗?当然是有工作的,但是凡是系统都会存在误差,尤其是当图像识别的结果和雷达结果有矛盾的时候,系统究竟应该相信哪一个的呢?我不知道特斯拉里面的代码是怎么写的,但显然在出现事故的例子当中,都选择相信了模型预测的结果,而把雷达的报警当成了误差。

      从外界看来,很难知道特斯拉这么设计包含了哪些苦衷,但都看得到血淋淋的事故。

      主流方案

      说完了特斯拉的方案,再来聊聊主流的方案。几乎可以这么说,除了特斯拉之外,没有任何一家汽车厂商采用纯图像识别的方案,采用的方案都大同小异,就是大家看到的车顶上顶了个雷达的方案。

      在这种方案当中,摄像头被当做了辅助,以测距的雷达为主。比如车顶上那个会旋转的雷达,是一个360°全景雷达,可以扫描车身周边整体的环境。除了车顶的雷达之外,车前方还有多个测距的雷达,有中程测距雷达,短程测距雷达等等。不同的雷达精度不一样,并且成本也不同,所以被用在了不同的地方。

      雷达

      据我所知,目前采用的雷达是激光雷达,这样的雷达扫描出来的结果是一个3D的点阵。最后车辆的自动驾驶的系统就是对这样一个点阵图使用各种技术进行分析,从而得出行驶的决策。由于汽车的形式速度很快,再加上雷达扫描的高帧率,带来的一个必然结果就是数据量爆炸。数据量虽然爆炸,但是车辆对数据分析的要求却不能降低,尤其是车身是在运动的,速度很快的话,0.1秒的延迟就可以酿成惨剧。所以计算的延迟必须非常低。

      但你会发现这其实是天然矛盾的,数据量已经很大了,还要求实时性,那就可以得出一个结论,这样的系统不可能全装在车上。我们在车上加装一个芯片是可行的,装一个电脑机箱问题也不大,但如果要装一个分布式的计算集群就离谱了。所以这样海量的数据计算只能放在云端,让云端的服务器集群来计算。但这样又带来了一个问题,数据处理放在云端了,就会有网络延迟,就好像大家打游戏一样。这个延迟是硬性的延迟,没有办法解决,只能等着通信技术提升,所以这就是为什么5G之后,自动驾驶才提上日程。

      地图

      雷达扫描的方案当中,相对来说准确性更高,出问题的概率要比图像识别低一些。但也同样有一些硬伤,一个比较大的硬伤是,缺乏地图信息。比如车辆要从A点去B点,我们通过各种地图可以知道路线,但是却不能知道路况。不知道这条路有多宽,是高架还是普通路,不知道几车道。不知道这些信息还如何行使呢?

      所以必须要知道,解决方案就是事先把路况信息扫描一遍,把这些信息存储起来。这就是常说的高清地图,其实也就是带上了更多信息的地图。根据我了解的消息说,目前的高清地图需要扫描全景,精确到毫米,要求非常高。要求一高带来的结果就是数据量爆炸,一小段路就能占用掉大量的存储。

      数据量一爆炸本身就能带来很多问题,最简单的例子就是说,我们不可能把原始数据直接丢进AI模型当中,什么样的模型也不可能接收这么多维度的特征,必须要经过一些处理。但如何处理,怎么处理,就是一个巨大的问题。

      控制

      自动驾驶另外一个很大的问题就是控制,很多程序员理解自动驾驶是一个算法调用汽车系统的各种api完成行驶,这么说倒也没错,但是实际情况要复杂得多。

      我们正常人在开车的时候,车辆拥有很多的传感器,把人的各种行为转化成驱动命令让车辆执行,这样的传感器称为ECU。在自动驾驶当中,驾驶系统直接给ECU发送指令,来控制汽车。但问题是汽车的ECU一般都有20个以上,我们让车辆执行指令的时候也不只是调用一个ECU,可能是多个ECU的组合。大家用排列组合简单算一下,就知道这当中的复杂度。举个简单的例子,你想要让车转弯90度,你不仅需要控制方向盘转向,还需要控制行驶的速度和转向时间。控制行驶的速度需要控制刹车和档位,转向时间则需要根据转弯半径以及汽车速度来估算,并不是跟汽车说转弯九十度,它就能执行的。

      不考虑自动行驶的算法部分,单单考虑车辆控制的部分,就是一个大工程,显然又需要加装很多传感器和器件。带来的结果就是系统复杂度进一步提升,提升的结果是会导致大量的热量。于是又需要加装散热模块,加装了这么多模块之后,你会发现一个很大的问题,就是系统缺电。现在汽油车的电力都是靠发动机发电来的,但内燃机的发电效率很低,要带动车辆本身的系统已经不容易了,又额外增加了这么多系统更是雪上加霜。所以很多专家看好电动车,因为电动车不存在这个问题。的确如此,电动车天然有电,但问题是,电动车假装这么一套系统耗电量也不小,续航会明显缩短,所以目前考虑到续航,各大厂商往往还是在汽油车或者是以汽油为主动力的车上改装。但这仍然是一个妥协的方案,并没有真正解决问题。

      其实了解自动驾驶了解得越多,越是会发现妥协的存在。类似的问题还有很多,倒不是说妥协不好,或者不应该妥协,而是想说这项技术的迈进远不像大家想的那么顺利。

      尾声

      说了这么许多,相信大家也应该感知到了自动驾驶的难度,的的确确是一个非常非常复杂的大工程。很多人因为了解到它的复杂性而退缩,觉得自动驾驶只是空话,短期内不会实现。但也有人迎难而上,选择攻克这一难关。

      索性现在国内的自动驾驶方案虽没有遥遥领先,但也是第一梯队,像是acm的顶级大佬楼教主等一众人才就专注在自动驾驶领域,我虽然无缘加入,但对他们的勇气和付出一样由衷地敬佩。也希望早日看到这项技术投产,有能力的话我一定支持。支持归支持,但有一点还是要说清楚。我们在享受自动或者是辅助驾驶的便利时也需要时刻保持安全警惕,技术的迭代和升级是需要代价的,一个产品从实验室里诞生之初一定不可能覆盖到所有的case,谁也不能保证你不会遇到系统意料之外的突发情况,所以一定要小心,享受科技,但也别过度相信科技。

      好了,今天的文章就到这里,感谢大家的阅读,喜欢的话不要忘了三连。

      ————————————————————————
      转载来源:公众号【TechFlow】

      posted in 聊一会吧
      Violet_恒源智享云
      Violet_恒源智享云

    Latest posts made by Violet_恒源智享云

    • RE: 【已解决】新手付费疑惑

      @159-8679 抱歉哦,给您带来不便。技术人员已经在查看了,稍等哈~

      posted in 用户反馈
      Violet_恒源智享云
      Violet_恒源智享云
    • RE: 开工了

      @131-7225 你好可怕

      posted in 聊一会吧
      Violet_恒源智享云
      Violet_恒源智享云
    • 【号外】听说一个好消息……福利要升级!很快了!

      555ff591-88b9-43e0-a3dc-da9948a71d56-wecom-temp-a52ce2071077593ba209a59df046456c.jpg

      这几天,Violet有点憋坏了😫
      事情是这样的
      前几天路过老板办公室,他好像在说学生福利要升级???😯
      好想立刻告诉你们,但是我怕被老板骂😭
      好煎熬😭

      然后,我想
      人生苦短,不吐不快!
      先斩后奏,他发现也来不及了,嘿嘿😛

      所以我知道的就是:
      学生福利要升级了!
      现在是95折,以后嘛……👍
      现在学生认证通过后领一次性代金券,以后嘛……🧧
      还有……😲

      嘿嘿,暂时就说这么多啦~~
      不怕,其实我也没说什么嘛,对吧!

      BTW
      还没认证的学生党们注意!!!
      👉
      8f5061d2-e372-4f53-97bb-48f0e5862623-wecom-temp-15f7006904acfef48624d54bdf9653ca.png

      答案很快就会揭晓啦!!🎉

      posted in 福利活动
      Violet_恒源智享云
      Violet_恒源智享云
    • RE: 大家都开始返校了吗?

      @183-8515 食堂开门了吗

      posted in 聊一会吧
      Violet_恒源智享云
      Violet_恒源智享云
    • 【平台维护公告】NAS存储升级维护公告

      69b11216-8566-4652-ba84-3bd6e8188789-1.png

      posted in 平台发布
      Violet_恒源智享云
      Violet_恒源智享云
    • RE: 【有奖话题NO.7】传说中的万能Transformer,你有用过吗?

      @155-7220 大佬真厉害

      posted in 有奖话题
      Violet_恒源智享云
      Violet_恒源智享云
    • RE: 开题+答辩

      加油呀,少年,祝你早日起飞~

      posted in 聊一会吧
      Violet_恒源智享云
      Violet_恒源智享云
    • RE: Deadline 才是第一生产力!!!

      4个小时就能写完一篇论文?天啊,我一篇800字作文都写不完😭

      posted in 聊一会吧
      Violet_恒源智享云
      Violet_恒源智享云
    • RE: 【有奖话题NO.6】大家使用云服务器,最常跑什么任务捏?

      有没有洒金子的任务可以跑的,教教我

      posted in 有奖话题
      Violet_恒源智享云
      Violet_恒源智享云
    • 【使用技巧】实例计费类型随时切换

      在使用过程中,可能会想更换计费类型,比如:

      1. 按量付费的实例切换成包周包月包年的实例;
      2. 包周包月包年切换成按量付费实例。
        下面我们就分别介绍一下这两种情况怎么操作。

      I. 按量付费的实例 ➡️ 包周包月包年的实例

      1. 对处于“运行中”状态的按量付费实例,点击“实例管理”-“转包周包月包年”

      2. 选择新的计费模式,可以看到对应计费模式的价格,以及最终支付的金额

      3. 点击“确定变更”,即可变更成功

      注意:仅部分机型支持包周包月包年。

      II. 包周包月包年 ➡️ 按量付费实例。

      1. 在实例列表中,点击付费方式列的“变更”
        6372c9e4-83e1-4870-a5d8-76a83f5066f2.png

      2. 在“到期管理”弹窗中,选择实例到期后希望转换的计费模式,及相应的价格。
        可以选择到期后:自动续费/ 关机/转按量付费

      3. 点击确定,设置完成。设置后的效果可以在列表中查看。

      posted in 新手教程
      Violet_恒源智享云
      Violet_恒源智享云