美文网首页
0#04 caffe 的使用

0#04 caffe 的使用

作者: dogo_L1L | 来源:发表于2019-02-26 18:34 被阅读0次

    1. 前言

        学习机器学习,一般并不会从 caffe 开始启蒙,一般都是使用 pytorch,tensorflow 等偏向于 python 的工具开始,所以讲解 caffe 的使用,默认大家对其他的 python 类开发工具有所了解.
        这次讲解 caffe 的使用,使用的是MNIST 手写数字识别,首先是因为,最容易上手,并且正确率能达到比较高的水平,另外就是 caffe 的example 路径下有 MNIST相关的内容.
    需要的预备知识:
        C/C++
        linux shell 脚本
    如果没有相关知识,也可以试着读一下.

    2. MNIST 数据文件下载

        在路径 "caffe 路径/examples/mnist" 路径下,含有完整的 caffe 的 MNIST 项目.
        在路径 "caffe 路径/build/examples/mnist" 路径下,有对 caffe 编译后,产生的对 caffe 的 MNIST 项目编译的结果.
    首先来看 下载 MNIST 的脚本: " caffe 路径/data/mnist/get_mnist.sh "

    #!/usr/bin/env sh
    # 这个脚本用于下载并解压 mnist 文件
    DIR="$( cd "$(dirname "$0")" ; pwd -P )"
    cd "$DIR"
    # printf
    echo "Downloading..."
    # for循环 设置要下载文件的名称
    for fname in train-images-idx3-ubyte train-labels-idx1-ubyte t10k-images-idx3-ubyte t10k-labels-idx1-ubyte
    do
        if [ ! -e $fname ]; then
        # 进行下载文件
            wget --no-check-certificate http://yann.lecun.com/exdb/mnist/${fname}.gz
        # 进行解压文件
            gunzip ${fname}.gz
        fi
    done
    

    使用 "./get_mnist.sh" 运行该程序.显示如下:

    lee@lee:~/Documents/caffe/data/mnist$ ./get_mnist.sh 
    Downloading...
    --2019-02-26 13:39:50--  http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz
    Resolving yann.lecun.com (yann.lecun.com)... 216.165.22.6
    Connecting to yann.lecun.com (yann.lecun.com)|216.165.22.6|:80... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 9912422 (9.5M) [application/x-gzip]
    Saving to: ‘train-images-idx3-ubyte.gz’
    
    train-images-idx3-u 100%[===================>]   9.45M   199KB/s    in 49s     
    
    2019-02-26 13:40:40 (197 KB/s) - ‘train-images-idx3-ubyte.gz’ saved [9912422/9912422]
    ......
    t10k-labels-idx1-ub 100%[===================>]   4.44K  --.-KB/s    in 0s      
    
    2019-02-26 13:40:52 (152 MB/s) - ‘t10k-labels-idx1-ubyte.gz’ saved [4542/4542]
    

    下载的数据是二进制格式,我们可以使用 vim 以二进制格式打开命令

    vim -b t10k-images-idx3-ubyte
    并在 vim 下方输入 ":%!xxd" 将文本文件强制以十六进制转换
    

    如下


    图片.png

    注:似乎,打开过文件会被破坏,建议删除重新执行一次.
    但是问题出现了,caffe 并不支持 二进制文件,只支持LMDB 或者levelDB,所以我们需要将数据进行转换.进行数据转换的脚本在 " caffe路径/examples/mnist/create_mnist.sh"
    进入到 " caffe路径 ",进行执行 "./examples/mnist/create_mnist.sh",显示如下:

    lee@lee:~/Documents/caffe$ ./examples/mnist/create_mnist.sh 
    Creating lmdb...
    I0226 14:39:08.853714  4944 db_lmdb.cpp:35] Opened lmdb examples/mnist/mnist_train_lmdb
    I0226 14:39:08.853905  4944 convert_mnist_data.cpp:88] A total of 60000 items.
    I0226 14:39:08.853924  4944 convert_mnist_data.cpp:89] Rows: 28 Cols: 28
    I0226 14:39:13.977677  4944 convert_mnist_data.cpp:108] Processed 60000 files.
    F0226 14:39:13.994539  4953 convert_mnist_data.cpp:59] Check failed: magic == 2051 (808464432 vs. 2051) Incorrect image file magic.
    *** Check failure stack trace: ***
        @     0x7f71094415cd  google::LogMessage::Fail()
        @     0x7f7109443433  google::LogMessage::SendToLog()
        @     0x7f710944115b  google::LogMessage::Flush()
        @     0x7f7109443e1e  google::LogMessageFatal::~LogMessageFatal()
        @           0x4031d3  convert_dataset()
        @           0x40226a  main
        @     0x7f71087c2b17  (unknown)
        @           0x4022ba  _start
        @              (nil)  (unknown)
    Aborted (core dumped)
    

    接下来分析一下 "create_mnist.sh" 文件

    #!/usr/bin/env sh
    # This script converts the mnist data into lmdb/leveldb format,
    # depending on the value assigned to $BACKEND.
    set -e
    # 定义一些变量
    EXAMPLE=examples/mnist
    DATA=data/mnist
    BUILD=build/examples/mnist
    
    BACKEND="lmdb"
    # printf
    echo "Creating ${BACKEND}..."
    
    # 下载之前,删除之前下载的内容
    rm -rf $EXAMPLE/mnist_train_${BACKEND}
    rm -rf $EXAMPLE/mnist_test_${BACKEND}
    
    # 将编译后的可执行文件,加上参数进行运行(主要内容)
    $BUILD/convert_mnist_data.bin $DATA/train-images-idx3-ubyte \
      $DATA/train-labels-idx1-ubyte $EXAMPLE/mnist_train_${BACKEND} --backend=${BACKEND}
    $BUILD/convert_mnist_data.bin $DATA/t10k-images-idx3-ubyte \
      $DATA/t10k-labels-idx1-ubyte $EXAMPLE/mnist_test_${BACKEND} --backend=${BACKEND}
    
    echo "Done."
    

    很明显最重要的内容就是

    $BUILD/convert_mnist_data.bin $DATA/train-images-idx3-ubyte \
      $DATA/train-labels-idx1-ubyte $EXAMPLE/mnist_train_${BACKEND} --backend=${BACKEND}
    $BUILD/convert_mnist_data.bin $DATA/t10k-images-idx3-ubyte \
      $DATA/t10k-labels-idx1-ubyte $EXAMPLE/mnist_test_${BACKEND} --backend=${BACKEND}
    

    但是 " convert_mnist_data.bin " 是编译后的二进制文件,并没有任何信息,所以我们应该去了解 " caffe目录/example/mnist/convert_mnist_data.cpp " 这个文件的内容.
    C 语言都是从 main 函数开始阅读.

    /* 主函数 包含 额外参数(int argc, char** argv)
       使用方法,
       convert_mnist_data.bin 图片文件路径 标签文件路径 生成数据库文件路径 --backend=使用数据库类型
       例如
       convert_mnist_data.bin t10k-images-ubyte t10k-labels-ubyte mnist_test_lmdb --backend=lmdb
    */
    int main(int argc, char** argv) {
    // 条件编译 实际中我们定义了 #include <gflags/gflags.h>
    #ifndef GFLAGS_GFLAGS_H_
      namespace gflags = google;
    #endif
    
      FLAGS_alsologtostderr = 1;
      // SetUsageMessage: 设置命令行帮助信息
      gflags::SetUsageMessage("This script converts the MNIST dataset to\n"
            "the lmdb/leveldb format used by Caffe to load data.\n"
            "Usage:\n"
            "    convert_mnist_data [FLAGS] input_image_file input_label_file "
            "output_db_file\n"
            "The MNIST dataset could be downloaded at\n"
            "    http://yann.lecun.com/exdb/mnist/\n"
            "You should gunzip them after downloading,"
            "or directly use data/mnist/get_mnist.sh\n");
      /* ParseCommandLineFlags: 解析命令行参数,
       * 与上文定义的 
       * DEFINE_string(backend, "lmdb", "The backend for storing the result");
       * 相对应
       * backend 变为 FLAGS_backend 变量
       */
      gflags::ParseCommandLineFlags(&argc, &argv, true);
    
      const string& db_backend = FLAGS_backend;
    
      if (argc != 4) {
        // 输出错误信息函数,第一个参数必须是 argv[0]
        gflags::ShowUsageWithFlagsRestrict(argv[0],
            "examples/mnist/convert_mnist_data");
      } else {
        // 初始化 glog
        google::InitGoogleLogging(argv[0]);
        // 进行数据转换(实际操作)
        convert_dataset(argv[1], argv[2], argv[3], db_backend);
      }
      return 0;
    }
    

    主函数中用到了大量的 gflags 和 glogs 的函数,都是用来对命令行与 日志相关,具体的真实干活的是 convert_dataset 函数.

    // convert_dataset 实际中 将 二进制文件内容 存储到 数据库 中的函数
    void convert_dataset(const char* image_filename, const char* label_filename,
            const char* db_path, const string& db_backend) {
      /* 以输入的方式 image_file 和 label_file
       * std::ifstream image_file 指 image_file 为一个类,并指定了文件路径和打开方法
       */
      std::ifstream image_file(image_filename, std::ios::in | std::ios::binary);
      std::ifstream label_file(label_filename, std::ios::in | std::ios::binary);
      // glog 中的 宏函数 如果 状态不正常 则输出后面内容
      CHECK(image_file) << "Unable to open file " << image_filename;
      CHECK(label_file) << "Unable to open file " << label_filename;
      //与二进制文件内容相关
      /* magic        魔数,类似于校验信息
       * num_items    条目(图片)的个数
       * num_labels   标签的个数
       * rows         一行元素中像素点个数
       * cols         一列元素中像素点个数
       */
      uint32_t magic;
      uint32_t num_items;
      uint32_t num_labels;
      uint32_t rows;
      uint32_t cols;
      // 使用 read 方法读取 image_file 前 (4*8)32 位元素的内容,即为魔数
      image_file.read(reinterpret_cast<char*>(&magic), 4);
      magic = swap_endian(magic);
      // 校验魔数是否一致
      CHECK_EQ(magic, 2051) << "Incorrect image file magic.";
      // 同理校验 label_file 的正确性
      label_file.read(reinterpret_cast<char*>(&magic), 4);
      magic = swap_endian(magic);
      CHECK_EQ(magic, 2049) << "Incorrect label file magic.";
      // 读取 条目的个数
      image_file.read(reinterpret_cast<char*>(&num_items), 4);
      num_items = swap_endian(num_items);
      // 读取标签的个数
      label_file.read(reinterpret_cast<char*>(&num_labels), 4);
      num_labels = swap_endian(num_labels);
      CHECK_EQ(num_items, num_labels);
      // 读取 条目(图片)的行像素点个数
      image_file.read(reinterpret_cast<char*>(&rows), 4);
      rows = swap_endian(rows);
      // 读取 条目(图片)的列像素点个数
      image_file.read(reinterpret_cast<char*>(&cols), 4);
      cols = swap_endian(cols);
    
      /* 来自
       * #include "boost/scoped_ptr.hpp"
       * 不用再分 levelDB 与LMDB
       */
      // 创建新 数据库DB
      scoped_ptr<db::DB> db(db::GetDB(db_backend));
      // 打开数据库,并指定数据库的路径
      db->Open(db_path, db::NEW);
      // 创建对数据库的 操作句柄(txn) Transaction 表示 事务,数据库内容的知识
      scoped_ptr<db::Transaction> txn(db->NewTransaction());
    
      // Storing to db
      char label;
      char* pixels = new char[rows * cols];
      int count = 0;
      string value;
      // 定义图片 对象
      Datum datum;
      // 图片的通道数
      datum.set_channels(1);
      // 图片的高
      datum.set_height(rows);
      // 图片的宽
      datum.set_width(cols);
      LOG(INFO) << "A total of " << num_items << " items.";
      LOG(INFO) << "Rows: " << rows << " Cols: " << cols;
      // 对 二进制文件中所有的 条目(图片) 进行处理
      for (int item_id = 0; item_id < num_items; ++item_id) {
        // 读取像素点
        image_file.read(pixels, rows * cols);
        // 读取标签
        label_file.read(&label, 1);
        datum.set_data(pixels, rows*cols);
        datum.set_label(label);
    
        string key_str = caffe::format_int(item_id, 8);
        // 对图片进行字符串序列化
        datum.SerializeToString(&value);
        // 通过事务 将 序列化后的图片信息 和 编号进行加入到数据库中
        txn->Put(key_str, value);
        // 并且 每 1000 个 提交一次
        if (++count % 1000 == 0) {
          txn->Commit();
        }
      }
      // 防止出现不能被1000整除的情况,对最后一批进行处理
      if (count % 1000 != 0) {
          txn->Commit();
      }
      LOG(INFO) << "Processed " << count << " files.";
      //删除 new 的内容
      delete[] pixels;
      // 关闭数据库
      db->Close();
    }
    

    convert_dataset 利用对 二进制文件的了解,对二进制文件进行解析,最后将其以数据库的形式保存.其中运用了一些数据库相关的知识,并且将数据库变成了面向对象的编程.
    解决最后的问题,数据的大小端问题.

    /* MNIST原始数据为大端存储,即数据的高字节保存在地址的低地址中,而数据的低字节保存在内存的高地址中
     * C/C++   数据为小端存储,和MNIST正好相反,
     * 所以需要定义一个函数进行转换
     */
    uint32_t swap_endian(uint32_t val) {
        val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF);
        return (val << 16) | (val >> 16);
    }
    

    到此 数据准备已经完成了.

    3. 训练数据

    开始训练生成的数据,训练脚本在"caffe路径/example/mnist/train_lenet.sh"
    其中调用了真正执行的语句

    #!/usr/bin/env sh
    set -e
    
    ./build/tools/caffe train --solver=examples/mnist/lenet_solver.prototxt
    

    "./example/mnist/train_lenet.sh"运行后,可能你会看见这样的显示:

    lee@lee:~/Documents/caffe$ ./build/tools/caffe train --solver=examples/mnist/lenet_solver.prototxt 
    I0226 16:01:17.276763 16197 caffe.cpp:204] Using GPUs 0
    F0226 16:01:17.277010 16197 common.cpp:66] Cannot use GPU in CPU-only Caffe: check mode.
    *** Check failure stack trace: ***
        @     0x7f32a8e065cd  google::LogMessage::Fail()
        @     0x7f32a8e08433  google::LogMessage::SendToLog()
        @     0x7f32a8e0615b  google::LogMessage::Flush()
        @     0x7f32a8e08e1e  google::LogMessageFatal::~LogMessageFatal()
        @     0x7f32a9373780  caffe::Caffe::SetDevice()
        @           0x40a45b  train()
        @           0x406f70  main
        @     0x7f32a7f83b17  (unknown)
        @           0x40779a  _start
        @              (nil)  (unknown)
    已放弃 (core dumped)
    

    这是因为默认在 " --solver=examples/mnist/lenet_solver.prototxt " 中写着:

    # caffe 求解模式: CPU or GPU
    solver_mode: GPU
    将其改为
    solver_mode: CPU
    

    就能正常运行
    运行中显示的内容提示:

    lee@lee:~/Documents/caffe$ ./examples/mnist/train_lenet.sh 
    I0226 16:32:44.089705 18561 caffe.cpp:197] Use CPU.使用CPU
    I0226 16:32:44.089968 18561 solver.cpp:45] 初始化超参数
    以下内容来自: "caffe/example/mnist/lenet_solver.prototxt(训练超参数文件) "
    test_iter: 100
    test_interval: 500
    .....
    snapshot: 5000
    snapshot_prefix: "examples/mnist/lenet"
    solver_mode: CPU
    net: "examples/mnist/lenet_train_test.prototxt"
    train_state {
      level: 0
      stage: ""
    }
    I0226 16:32:44.090420 18561 solver.cpp:102] Creating training net from net file: examples/mnist/lenet_train_test.prototxt 
    按照  " examples/mnist/lenet_train_test.prototxt " 文件建立网络
    I0226 16:32:44.090696 18561 net.cpp:296] The NetState phase (0) differed from the phase (1) specified by a rule in layer mnist
    I0226 16:32:44.090768 18561 net.cpp:296] The NetState phase (0) differed from the phase (1) specified by a rule in layer accuracy
    I0226 16:32:44.090971 18561 net.cpp:53] Initializing net from parameters: 初始化网络参数
    以下内容来自 " examples/mnist/lenet_train_test.prototxt "
    name: "LeNet"
    state {    # 创建训练网络
      phase: TRAIN
      level: 0
      stage: ""
    }
    layer {
      name: "mnist"
      type: "Data"
      top: "data"
      top: "label"
      include {
        phase: TRAIN
      }
      transform_param {
        scale: 0.00390625
      }
      data_param {
        source: "examples/mnist/mnist_train_lmdb"  # 使用到的数据库路径
        batch_size: 64
        backend: LMDB
      }
    }
    ......
    layer {
      name: "loss"
      type: "SoftmaxWithLoss"
      bottom: "ip2"
      bottom: "label"
      top: "loss"
    }
    I0226 16:32:44.097754 18561 layer_factory.hpp:77] Creating layer mnist
    I0226 16:32:44.097884 18561 db_lmdb.cpp:35] Opened lmdb examples/mnist/mnist_train_lmdb
    创建 各种训练 层
    I0226 16:32:44.097921 18561 net.cpp:86] Creating Layer mnist
    ......
    # 显示当前内存占有
    I0226 16:32:44.098275 18561 net.cpp:139] Memory required for data: 200960
    ......
    进行反馈回路
    I0226 16:32:44.104403 18561 layer_factory.hpp:77] Creating layer loss
    I0226 16:32:44.104418 18561 net.cpp:86] Creating Layer loss
    I0226 16:32:44.104429 18561 net.cpp:408] loss <- ip2
    I0226 16:32:44.104440 18561 net.cpp:408] loss <- label
    I0226 16:32:44.104460 18561 net.cpp:382] loss -> loss
    I0226 16:32:44.104496 18561 layer_factory.hpp:77] Creating layer loss
    I0226 16:32:44.104522 18561 net.cpp:124] Setting up loss
    I0226 16:32:44.104537 18561 net.cpp:131] Top shape: (1)
    I0226 16:32:44.104553 18561 net.cpp:134]     with loss weight 1
    I0226 16:32:44.104581 18561 net.cpp:139] Memory required for data: 5169924
    I0226 16:32:44.104593 18561 net.cpp:200] loss needs backward computation.
    .....
    I0226 16:32:44.104709 18561 net.cpp:244] This network produces output loss
    I0226 16:32:44.104732 18561 net.cpp:257] Network initialization done.
    创建 测试 网络(同上)
    ......
    开始迭代(每100次显示一次,每500次保存一次)
    I0226 16:32:47.178556 18561 solver.cpp:239] Iteration 0 (-4.34403e-44 iter/s, 3.065s/100 iters), loss = 2.3588
    loss值
    I0226 16:32:47.178653 18561 solver.cpp:258]     Train net output #0: loss = 2.3588 (* 1 = 2.3588 loss)
    ......
    

    上面的指令中 重要的部分是 " --solver=examples/mnist/lenet_solver.prototxt "
    examples/mnist/lenet_solver.prototxt 是使用 protobuffer 编写的训练超参数文件.文件中指定的超参数如下:

    # 训练与测试 网络的 prototxt 文件所在位置
    net: "examples/mnist/lenet_train_test.prototxt"
    # test_iter specifies how many forward passes the test should carry out.
    # In the case of MNIST, we have test batch size 100 and 100 test iterations,
    # covering the full 10,000 testing images.
    # 测试截断迭代次数
    test_iter: 100
    # 训练时每迭代500次进行一次预测
    test_interval: 500
    # 网络的基础学习速率,冲量,权衰量
    base_lr: 0.01
    momentum: 0.9
    weight_decay: 0.0005
    # 学习速率的衰减策略
    lr_policy: "inv"
    gamma: 0.0001
    power: 0.75
    # 每100次迭代,屏幕打印一次
    display: 100
    # 最大的迭代次数
    max_iter: 10000
    # 每5000次迭代打印一次快照
    snapshot: 5000
    #快照保存位置
    snapshot_prefix: "examples/mnist/lenet"
    # 求解模式选择(CPU 或者GPU)
    solver_mode: CPU
    

    同样的 在超参数文档中指定了网络结构 net: "examples/mnist/lenet_train_test.prototxt" 也是使用了lenet_train_test.prototxt 也是 prototxt 格式

    5. 训练 网络 分析

    上文提到了,训练网络位于 "examples/mnist/lenet_train_test.prototxt" 也是使用 protoBuffer 格式.
    那这就来看看 " lenet_train_test.prototxt " 中的内容

    # 网络名称为 LeNet
    name: "LeNet"
    layer {
    # 层的名称
      name: "mnist"
    # 层的类型为:数据层
      type: "Data"
    # top 表示输出
    # 输出 data 和label
      top: "data"
      top: "label"
      include {
    # 该层只有训练阶段有效
        phase: TRAIN
      }
      transform_param {
    # 数据变换使用的缩放因子
        scale: 0.00390625
      }
    # 数据来源参数
      data_param {
        source: "examples/mnist/mnist_train_lmdb"
        batch_size: 64
        backend: LMDB
      }
    }
    layer {
      name: "mnist"
      type: "Data"
      top: "data"
      top: "label"
      include {
    # 同上,该层只有测试阶段有效
        phase: TEST
      }
      transform_param {
        scale: 0.00390625
      }
      data_param {
        source: "examples/mnist/mnist_test_lmdb"
        batch_size: 100
        backend: LMDB
      }
    }
    # 卷积层
    layer {
      name: "conv1"
    # 层的类型为:卷积层
      type: "Convolution"
    # bottom 表示输入
      bottom: "data"
    # top 表示输出
      top: "conv1"
    # 第一个 pram 表示权值学习速率倍乘因子
      param {
        lr_mult: 1
      }
    # 第二个 pram 表示bias(偏移量)学习速率倍乘因子
      param {
        lr_mult: 2
      }
    # 卷积计算参数
      convolution_param {
    # 输出 的 通道数
        num_output: 20
    # 卷积核大小
        kernel_size: 5
    # 卷积的步长
        stride: 1
    # 权值使用 xavier 填充器
        weight_filler {
          type: "xavier"
        }
    # 偏移量使用 常数填充器,默认为0
        bias_filler {
          type: "constant"
        }
      }
    }
    
    #池化层
    layer {
      name: "pool1"
    # 层的类型为:池化层
      type: "Pooling"
    # 输入输出
      bottom: "conv1"
      top: "pool1"
    # 池化层参数
      pooling_param {
    # 使用最大池化
        pool: MAX
    # 池化核大小
        kernel_size: 2
    # 池化核移动步长
        stride: 2
      }
    }
    ......
    # 全连接层
    layer {
      name: "ip1"
    # 层的类型为:全连接层
      type: "InnerProduct"
      bottom: "pool2"
      top: "ip1"
      param {
        lr_mult: 1
      }
      param {
        lr_mult: 2
      }
    # 全连接层参数
      inner_product_param {
    # 输出通道数
        num_output: 500
        weight_filler {
          type: "xavier"
        }
        bias_filler {
          type: "constant"
        }
      }
    }
    # 非线性层
    layer {
      name: "relu1"
    # 层的类型为:非线性层
      type: "ReLU"
    # 输入输出信息
      bottom: "ip1"
      top: "ip1"
    }
    ......
    # 准确率计算层
    layer {
      name: "accuracy"
    # 层的类型为:准确率计算层
      type: "Accuracy"
      bottom: "ip2"
      bottom: "label"
      top: "accuracy"
      include {
    # 只有 测试阶段 有效
        phase: TEST
      }
    }
    # 损失层
    layer {
      name: "loss"
    # 层的类型为:softmaxloss 损失值计算层
      type: "SoftmaxWithLoss"
      bottom: "ip2"
      bottom: "label"
      top: "loss"
    }
    

    6. caffe 的 一些 技巧

    既然已经学习了一些其他的 机器学习框架,只是在另一个框架进行套用,只是一些名称的不同,所以我们还要搞清楚 caffe 支持哪些层.
    在 我们安装的 "caffe 路径/docs/tutorial/layers" 这个路径下就有所有层的介绍

    lee@lee:~/Documents/caffe/docs/tutorial/layers$ tree .
    .
    ├── absval.md
    ├── accuracy.md
    ├── argmax.md
    ├── batchnorm.md
    ├── batchreindex.md
    ├── bias.md
    ├── bnll.md
    ├── clip.md
    ├── concat.md
    ├── contrastiveloss.md
    ├── convolution.md
    ├── crop.md
    ├── data.md
    ├── deconvolution.md
    ├── dropout.md
    ├── dummydata.md
    ├── eltwise.md
    ├── elu.md
    ├── embed.md
    ├── euclideanloss.md
    ├── exp.md
    ├── filter.md
    ├── flatten.md
    ├── hdf5data.md
    ├── hdf5output.md
    ├── hingeloss.md
    ├── im2col.md
    ├── imagedata.md
    ├── infogainloss.md
    ├── innerproduct.md
    ├── input.md
    ├── log.md
    ├── lrn.md
    ├── lstm.md
    ├── memorydata.md
    ├── multinomiallogisticloss.md
    ├── mvn.md
    ├── parameter.md
    ├── pooling.md
    ├── power.md
    ├── prelu.md
    ├── python.md
    ├── recurrent.md
    ├── reduction.md
    ├── relu.md
    ├── reshape.md
    ├── rnn.md
    ├── scale.md
    ├── sigmoidcrossentropyloss.md
    ├── sigmoid.md
    ├── silence.md
    ├── slice.md
    ├── softmax.md
    ├── softmaxwithloss.md
    ├── split.md
    ├── spp.md
    ├── tanh.md
    ├── threshold.md
    ├── tile.md
    └── windowdata.md
    

    这也局限了 caffe 比如 SSD 算法的输出并不是固定的,对于 SSD 需要专门的 caffe-SSD 的支持.
    同样 prototxt 带来了很多的好处,比如可以优秀的可视化工具,连接如下:
    http://ethereon.github.io/netscope/#/editor

    图片.png
    出现一个报错,
    解决方案:netscope,Can't infer network data shapes

    相关文章

      网友评论

          本文标题:0#04 caffe 的使用

          本文链接:https://www.haomeiwen.com/subject/jcowyqtx.html