Repository: incubator-singa
Updated Branches:
  refs/heads/master 714fd2cb3 -> bb75a0be5


SINGA-98 Add Support for AlexNet ImageNet Classification Model

Create the workspace for alexnet, i.e., examples/alexnet
* job.conf for training alexnet on CPU. TODO check the configuration
* im2rec convert image to binary record
* rec2im extract image from binary record for checking the correctness of im2rec


Project: http://git-wip-us.apache.org/repos/asf/incubator-singa/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-singa/commit/fe86b02a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-singa/tree/fe86b02a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-singa/diff/fe86b02a

Branch: refs/heads/master
Commit: fe86b02ae8748f3534e2b6e3197b163c367fed59
Parents: 714fd2c
Author: Wang Ji <[email protected]>
Authored: Fri Dec 25 18:47:56 2015 +0800
Committer: Wei Wang <[email protected]>
Committed: Wed Jan 6 00:48:13 2016 +0800

----------------------------------------------------------------------
 examples/alexnet/Makefile.example |  31 +++
 examples/alexnet/im2rec.cc        | 136 ++++++++++++
 examples/alexnet/job.conf         | 380 +++++++++++++++++++++++++++++++++
 examples/alexnet/rec2im_test.cc   |  95 +++++++++
 4 files changed, 642 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/fe86b02a/examples/alexnet/Makefile.example
----------------------------------------------------------------------
diff --git a/examples/alexnet/Makefile.example 
b/examples/alexnet/Makefile.example
new file mode 100644
index 0000000..78ca7c3
--- /dev/null
+++ b/examples/alexnet/Makefile.example
@@ -0,0 +1,31 @@
+#/**
+# * Copyright 2015 The Apache Software Foundation
+# *
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements.  See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership.  The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License.  You may obtain a copy of the License at
+# *
+# *     http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+libs :=singa glog protobuf
+
+.PHONY: all create
+
+create:
+       $(CXX) im2rec.cc `pkg-config opencv --cflags --libs` -std=c++11 -lsinga 
-lprotobuf -lglog \
+               -I../../include -L../../.libs/ 
-Wl,-unresolved-symbols=ignore-in-shared-libs \
+               -Wl,-rpath=../../.libs/  -o im2rec.bin
+       $(CXX) rec2im_test.cc `pkg-config opencv --cflags --libs` -std=c++11 
-lsinga -lprotobuf -lglog \
+               -I../../include -L../../.libs/ 
-Wl,-unresolved-symbols=ignore-in-shared-libs \
+               -Wl,-rpath=../../.libs/  -o rec2im_test.bin

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/fe86b02a/examples/alexnet/im2rec.cc
----------------------------------------------------------------------
diff --git a/examples/alexnet/im2rec.cc b/examples/alexnet/im2rec.cc
new file mode 100644
index 0000000..cf6eedf
--- /dev/null
+++ b/examples/alexnet/im2rec.cc
@@ -0,0 +1,136 @@
+#include <glog/logging.h>
+#include <algorithm>
+#include <random>
+#include <chrono>
+#include <fstream>
+#include <string>
+#include <cstdint>
+#include <iostream>
+#include <vector>
+#include <opencv2/opencv.hpp>
+
+#include "singa/io/store.h"
+#include "singa/proto/common.pb.h"
+
+using std::string;
+
+const int kImageSize = 256;
+const int kImageNBytes = 256*256*3;
+
+void create_data(const string& image_list,
+    const string& input_folder,
+    const string& output_folder,
+    const string& backend = "kvfile")
+{
+  singa::RecordProto image;
+  image.add_shape(3);
+  image.add_shape(kImageSize);
+  image.add_shape(kImageSize);
+
+  singa::RecordProto mean;
+  mean.CopyFrom(image);
+  for (int i = 0; i < kImageNBytes; ++i)
+    mean.add_data(0.f);
+
+  auto store = singa::io::CreateStore(backend);
+  if (backend == "lmdb")
+    CHECK(store->Open(output_folder + "/image_record", singa::io::kCreate));
+  else
+    CHECK(store->Open(output_folder + "/image_record.bin", 
singa::io::kCreate));
+
+  LOG(INFO) << "Generating image record";
+
+  std::ifstream image_list_file(image_list.c_str(), std::ios::in);
+  CHECK(image_list_file.is_open()) << "Unable to open image list";
+
+  string image_file_name;
+  int label;
+  char str_buffer[kImageNBytes];
+  string rec_buf;
+  cv::Mat img, res;
+  std::vector<std::pair<string, int>> file_list;
+  while(image_list_file >> image_file_name >> label)
+    file_list.push_back(std::make_pair(image_file_name, label));
+  LOG(INFO) << "Data Shuffling";
+  unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
+  std::shuffle(file_list.begin(), file_list.end()
+      ,std::default_random_engine());
+  LOG(INFO) << "Total number of images is " << file_list.size();
+  int ImageNum = file_list.size();
+
+  for (int imageid = 0; imageid < ImageNum; ++imageid) {
+    string path = input_folder + "/" + file_list[imageid].first;
+    img = cv::imread(path, CV_LOAD_IMAGE_COLOR);
+    CHECK(img.data != NULL) << "OpenCV load image fail" << path;
+    cv::resize(img, res, cv::Size(kImageSize, kImageSize),
+        0, 0, CV_INTER_LINEAR);
+    for (int h = 0; h < kImageSize; ++h) {
+      const uchar* ptr = res.ptr<uchar>(h);
+      int img_index = 0;
+      for (int w = 0; w < kImageSize; ++w)
+        for (int c = 0; c < 3; ++c)
+          str_buffer[(c*kImageSize+h)*kImageSize+w] =
+            static_cast<uint8_t>(ptr[img_index++]);
+    }
+    /*
+    for (int i = 0; i < kImageSize; ++i) {
+      for (int j = 0; j < kImageSize; ++j) {
+        cv::Vec3b pixel = res.at<cv::Vec3b>(j, i);
+        str_buffer[i*kImageSize+j] = static_cast<uint8_t>(pixel.val[2]);
+        str_buffer[kImageSize*kImageSize+i*kImageSize+j] = 
static_cast<uint8_t>(pixel.val[1]);
+        str_buffer[kImageSize*kImageSize*2+i*kImageSize+j] = 
static_cast<uint8_t>(pixel.val[0]);
+      }
+    }
+    */
+    image.set_label(file_list[imageid].second);
+    image.set_pixel(str_buffer, kImageNBytes);
+    image.SerializeToString(&rec_buf);
+
+    int length = snprintf(str_buffer, kImageNBytes, "%08d", imageid);
+    CHECK(store->Write(string(str_buffer, length), rec_buf));
+    if ((imageid+1) % 1000 == 0) {
+      store->Flush();
+      LOG(INFO) << imageid+1 << " files processed.";
+    }
+    const string& pixels = image.pixel();
+    for (int i = 0; i < kImageNBytes; ++i)
+      mean.set_data(i, mean.data(i) + static_cast<uint8_t>(pixels[i]));
+  }
+  if (ImageNum % 1000 != 0)
+      LOG(INFO) << ImageNum << " files processed.";
+
+  store->Flush();
+  store->Close();
+
+  LOG(INFO) << "Create image mean";
+  if (backend == "lmdb")
+    CHECK(store->Open(output_folder + "/image_mean", singa::io::kCreate));
+  else
+    CHECK(store->Open(output_folder + "/image_mean.bin", singa::io::kCreate));
+  for (int i = 0; i < kImageNBytes; i++)
+    mean.set_data(i, mean.data(i) / ImageNum);
+  mean.SerializeToString(&rec_buf);
+  store->Write("mean", rec_buf);
+  store->Flush();
+  store->Close();
+  delete store;
+
+  LOG(INFO) << "Done!";
+}
+
+int main(int argc, char** argv) {
+  if (argc < 4) {
+    std::cout << "Create Datashard for ImageNet dataset.\n"
+      << "Usage: <image_list> <input_folder> <output_folder>"
+      << " <Optional: backend {lmdb, kvfile} default: kvfile>\n";
+  } else {
+    google::InitGoogleLogging(argv[0]);
+    FLAGS_alsologtostderr = 1;
+    if (argc == 4)
+      create_data(string(argv[1]), string(argv[2]), string(argv[3]));
+    else
+      create_data(string(argv[1]), string(argv[2]),
+          string(argv[3]), string(argv[4]));
+  }
+  return 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/fe86b02a/examples/alexnet/job.conf
----------------------------------------------------------------------
diff --git a/examples/alexnet/job.conf b/examples/alexnet/job.conf
new file mode 100644
index 0000000..b8ac6c7
--- /dev/null
+++ b/examples/alexnet/job.conf
@@ -0,0 +1,380 @@
+name: "alexnet"
+train_steps: 100000
+test_steps: 10
+test_freq: 300
+disp_freq: 100
+#checkpoint_path: "examples/alexnet/checkpoint/step10000-worker0"
+train_one_batch {
+  alg: kBP
+}
+updater{
+  type: kSGD
+  weight_decay: 0.0005
+  momentum: 0.9
+  learning_rate {
+    type: kFixed
+    base_lr: 0.9
+  }
+}
+neuralnet {
+  layer{
+    name: "data"
+    type: kRecordInput
+    store_conf {
+      backend: "kvfile"
+      path :"/data/dataset/train_record.bin"
+      mean_file: "/data/dataset/train_mean.bin"
+      batchsize: 256
+      random_skip: 5000
+      shape: 3
+      shape: 256
+      shape: 256
+    }
+      include: kTrain
+  }
+  layer{
+    name: "data"
+    type: kRecordInput
+    store_conf {
+      backend: "kvfile"
+      path :"/data/dataset/val_record.bin"
+      mean_file: "/data/dataset/val_mean.bin"
+      batchsize: 256
+      shape: 3
+      shape: 256
+      shape: 256
+    }
+      include: kTest
+  }
+  layer{
+    name: "image"
+    type: kImagePreprocess
+    rgbimage_conf {
+      cropsize: 227
+      mirror: true
+    }
+#    partition_dim: 0
+    srclayers: "data"
+  }
+  layer{
+    name: "conv1"
+    type: kCConvolution
+    srclayers: "image"
+    convolution_conf {
+      num_filters: 96
+      kernel: 11
+      stride: 4
+    }
+#    partition_dim: 0
+    param {
+      name: "w1"
+      init {
+        type: kGaussian
+        std: 0.01
+      }
+    }
+    param {
+      name: "b1"
+      init {
+        type: kConstant
+        value: 0
+      }
+    }
+  }
+  layer {
+    name: "relu1"
+    type: kReLU
+    srclayers: "conv1"
+#    partition_dim: 0
+  }
+  layer {
+    name: "norm1"
+    type: kLRN
+    lrn_conf {
+      local_size: 5
+      alpha: 0.0001
+      beta: 0.75
+      knorm: 2
+    }
+    srclayers: "relu1"
+#    partition_dim: 0
+  }
+  layer {
+    name: "pool1"
+    type: kCPooling
+    pooling_conf {
+      pool: MAX
+      kernel: 3
+      stride: 2
+    }
+    srclayers: "norm1"
+#    partition_dim: 0
+  }
+  layer{
+    name: "conv2"
+    type: kCConvolution
+    srclayers: "pool1"
+    convolution_conf {
+      num_filters: 256
+      kernel: 5
+      pad: 2
+    }
+#    partition_dim: 0
+    param {
+      name: "w2"
+      init {
+        type: kGaussian
+        std: 0.01
+      }
+    }
+    param {
+      name: "b2"
+      init {
+        type: kConstant
+        value: 0
+      }
+    }
+  }
+  layer {
+    name: "relu2"
+    type: kReLU
+    srclayers: "conv2"
+#    partition_dim: 0
+  }
+  layer {
+    name: "norm2"
+    type: kLRN
+    lrn_conf {
+      local_size: 5
+      alpha: 0.0001
+      beta: 0.75
+      knorm: 2
+    }
+    srclayers: "relu2"
+#    partition_dim: 0
+  }
+  layer {
+    name: "pool2"
+    type: kCPooling
+    pooling_conf {
+      pool: MAX
+      kernel: 3
+      stride: 2
+    }
+    srclayers: "norm2"
+#    partition_dim: 0
+  }
+  layer{
+    name: "conv3"
+    type: kCConvolution
+    srclayers: "pool2"
+    convolution_conf {
+      num_filters: 384
+      kernel: 3
+      pad: 1
+    }
+#    partition_dim: 0
+    param {
+      name: "w3"
+      init {
+        type: kGaussian
+        std: 0.01
+      }
+    }
+    param {
+      name: "b3"
+      init {
+        type: kConstant
+        value: 0
+      }
+    }
+  }
+  layer {
+    name: "relu3"
+    type: kReLU
+    srclayers: "conv3"
+#    partition_dim: 0
+  }
+  layer{
+    name: "conv4"
+    type: kCConvolution
+    srclayers: "relu3"
+    convolution_conf {
+      num_filters: 384
+      kernel: 3
+      pad: 1
+    }
+#    partition_dim: 0
+    param {
+      name: "w4"
+      init {
+        type: kGaussian
+        std: 0.01
+      }
+    }
+    param {
+      name: "b4"
+      init {
+        type: kConstant
+        value: 0
+      }
+    }
+  }
+  layer {
+    name: "relu4"
+    type: kReLU
+    srclayers: "conv4"
+#    partition_dim: 0
+  }
+  layer{
+    name: "conv5"
+    type: kCConvolution
+    srclayers: "relu4"
+    convolution_conf {
+      num_filters: 256
+      kernel: 3
+      pad: 1
+    }
+#    partition_dim: 0
+    param {
+      name: "w5"
+      init {
+        type: kGaussian
+        std: 0.01
+      }
+    }
+    param {
+      name: "b5"
+      init {
+        type: kConstant
+        value: 0
+      }
+    }
+  }
+  layer {
+    name: "relu5"
+    type: kReLU
+    srclayers: "conv5"
+#    partition_dim: 0
+  }
+  layer {
+    name: "pool5"
+    type: kCPooling
+    pooling_conf {
+      pool: MAX
+      kernel: 3
+      stride: 2
+    }
+    srclayers: "relu5"
+#    partition_dim: 0
+  }
+  layer {
+    name: "ip6"
+    type: kInnerProduct
+    innerproduct_conf {
+      num_output: 4096
+    }
+    param {
+      name: "w6"
+      init {
+        type: kGaussian
+        std: 0.01
+      }
+    }
+    param {
+      name: "b6"
+      init {
+        type: kConstant
+        value: 1
+      }
+    }
+    srclayers: "pool5"
+#    partition_dim: 1
+  }
+  layer {
+    name: "relu6"
+    type: kReLU
+    srclayers: "ip6"
+#    partition_dim: 1
+  }
+  layer {
+    name: "drop6"
+    type: kDropout
+    srclayers: "relu6"
+#    partition_dim: 1
+  }
+  layer {
+    name: "ip7"
+    type: kInnerProduct
+    innerproduct_conf {
+      num_output: 4096
+    }
+#    partition_dim: 1
+    param {
+      name: "w7"
+      init {
+        type: kGaussian
+        std: 0.01
+      }
+    }
+    param {
+      name: "b7"
+      init {
+        type: kConstant
+        value: 1
+      }
+    }
+    srclayers: "drop6"
+  }
+  layer {
+    name: "relu7"
+    type: kReLU
+    srclayers: "ip7"
+#    partition_dim: 1
+  }
+  layer {
+    name: "drop7"
+    type: kDropout
+    srclayers: "relu7"
+#    partition_dim: 1
+  }
+  layer {
+    name: "ip8"
+    type: kInnerProduct
+    innerproduct_conf {
+      num_output: 1000
+    }
+#    partition_dim: 1
+    param {
+      name: "w8"
+      init {
+        type: kGaussian
+        std: 0.01
+      }
+    }
+    param {
+      name: "b8"
+      init {
+        type: kConstant
+        value: 1
+      }
+    }
+    srclayers: "drop7"
+  }
+  layer {
+    name: "loss"
+    type: kSoftmaxLoss
+    softmaxloss_conf {
+      topk:1
+    }
+    srclayers: "ip8"
+    srclayers: "data"
+  }
+}
+cluster {
+  nworker_groups: 1
+  nserver_groups: 1
+  nworkers_per_group: 1
+  nworkers_per_procs: 1
+  workspace: "examples/alexnet"
+}

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/fe86b02a/examples/alexnet/rec2im_test.cc
----------------------------------------------------------------------
diff --git a/examples/alexnet/rec2im_test.cc b/examples/alexnet/rec2im_test.cc
new file mode 100644
index 0000000..0fa3505
--- /dev/null
+++ b/examples/alexnet/rec2im_test.cc
@@ -0,0 +1,95 @@
+#include <glog/logging.h>
+#include <algorithm>
+#include <random>
+#include <chrono>
+#include <fstream>
+#include <string>
+#include <cstdint>
+#include <iostream>
+#include <vector>
+#include <opencv2/opencv.hpp>
+
+#include "singa/io/store.h"
+#include "singa/proto/common.pb.h"
+
+using std::string;
+
+const int kImageSize = 256;
+const int kImageNBytes = 256*256*3;
+
+void generate_image(const string& output_folder,
+    const string& key,
+    const string& val)
+{
+  float image_buf[kImageNBytes];
+  singa::RecordProto image;
+  image.ParseFromString(val);
+  cv::Mat img = cv::Mat::zeros(kImageSize, kImageSize, CV_8UC3);
+  string pixel = image.pixel();
+  int label = image.label();
+  string image_name = output_folder+"/"+key+"_"+std::to_string(label)+".jpg";
+  std::cout << "Writing to " << image_name << "...\n";
+  for (int h = 0; h < kImageSize; ++h) {
+    uchar* ptr = img.ptr<uchar>(h);
+    int img_index = 0;
+    for (int w = 0; w < kImageSize; ++w) {
+      for (int c = 0; c < 3; ++c)
+        ptr[img_index++] =
+          static_cast<uchar>(
+              static_cast<uint8_t>(
+                pixel[(c * kImageSize + h) * kImageSize + w]));
+  }
+
+  cv::imwrite(image_name, img);
+}
+
+void visualize(const string& input_file,
+    const string& output_folder,
+    const string& id_list)
+{
+  auto store = singa::io::OpenStore("kvfile", input_file,
+      singa::io::kRead);
+
+  std::vector<int> image_id_list;
+
+  std::ifstream id_list_file(id_list.c_str(), std::ios::in);
+  CHECK(id_list_file.is_open()) << "Unable to open image id list";
+  string id_;
+  while(id_list_file >> id_) {
+    int x;
+    x = std::stoi(id_);
+    image_id_list.push_back(x);
+  }
+  std::sort(image_id_list.begin(), image_id_list.end());
+
+  string key, val;
+  for (int i = 0; i < image_id_list[0]; ++i)
+    if (!store->Read(&key, &val)) {
+      store->SeekToFirst();
+      CHECK(store->Read(&key, &val));
+    }
+  generate_image(output_folder, key, val);
+
+  for (size_t i = 1; i != image_id_list.size(); ++i) {
+    for (int j = 0; j < image_id_list[i]-image_id_list[i-1]; ++j)
+      if (!store->Read(&key, &val)) {
+        store->SeekToFirst();
+        CHECK(store->Read(&key, &val));
+      }
+    generate_image(output_folder, key, val);
+  }
+}
+
+int main(int argc, char** argv)
+{
+  if (argc != 4) {
+    std::cout << "Visualize images from binary kvfile record.\n"
+      << "Usage: <input_file> <output_folder> <id_list>\n";
+  } else {
+    google::InitGoogleLogging(argv[0]);
+    FLAGS_alsologtostderr = 1;
+    visualize(string(argv[1]), string(argv[2]), string(argv[3]));
+  }
+
+  return 0;
+}

Reply via email to