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; +}
