Skip to content

Commit 6e8eb5e

Browse files
committed
Get cifar10 tests up and running
1 parent 2f39f75 commit 6e8eb5e

File tree

8 files changed

+337
-4
lines changed

8 files changed

+337
-4
lines changed

build.sbt

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import AssemblyKeys._
2+
3+
assemblySettings
4+
5+
libraryDependencies += "net.java.dev.jna" % "jna" % "4.2.1"
6+
7+
libraryDependencies += "org.scalatest" % "scalatest_2.10" % "2.0" % "test"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# reduce learning rate after 120 epochs (60000 iters) by factor 0f 10
2+
# then another factor of 10 after 10 more epochs (5000 iters)
3+
4+
# The train/test net protocol buffer definition
5+
net: "examples/cifar10/cifar10_full_java_train_test.prototxt"
6+
# test_iter specifies how many forward passes the test should carry out.
7+
# In the case of CIFAR10, we have test batch size 100 and 100 test iterations,
8+
# covering the full 10,000 testing images.
9+
test_iter: 100
10+
# Carry out testing every 1000 training iterations.
11+
test_interval: 1000
12+
# The base learning rate, momentum and the weight decay of the network.
13+
base_lr: 0.001
14+
momentum: 0.9
15+
weight_decay: 0.004
16+
# The learning rate policy
17+
lr_policy: "fixed"
18+
# Display every 200 iterations
19+
display: 200
20+
# The maximum number of iterations
21+
max_iter: 60000
22+
# solver mode: CPU or GPU
23+
solver_mode: GPU
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
name: "CIFAR10_full"
2+
layer {
3+
name: "cifardata"
4+
type: "JavaData"
5+
top: "data"
6+
java_data_param {
7+
shape {
8+
dim: 100
9+
dim: 3
10+
dim: 32
11+
dim: 32
12+
}
13+
}
14+
}
15+
layer {
16+
name: "cifarlabel"
17+
type: "JavaData"
18+
top: "label"
19+
java_data_param {
20+
shape {
21+
dim: 100
22+
dim: 1
23+
}
24+
}
25+
}
26+
layer {
27+
name: "conv1"
28+
type: "Convolution"
29+
bottom: "data"
30+
top: "conv1"
31+
param {
32+
lr_mult: 1
33+
}
34+
param {
35+
lr_mult: 2
36+
}
37+
convolution_param {
38+
num_output: 32
39+
pad: 2
40+
kernel_size: 5
41+
stride: 1
42+
weight_filler {
43+
type: "gaussian"
44+
std: 0.0001
45+
}
46+
bias_filler {
47+
type: "constant"
48+
}
49+
}
50+
}
51+
layer {
52+
name: "pool1"
53+
type: "Pooling"
54+
bottom: "conv1"
55+
top: "pool1"
56+
pooling_param {
57+
pool: MAX
58+
kernel_size: 3
59+
stride: 2
60+
}
61+
}
62+
layer {
63+
name: "relu1"
64+
type: "ReLU"
65+
bottom: "pool1"
66+
top: "pool1"
67+
}
68+
layer {
69+
name: "norm1"
70+
type: "LRN"
71+
bottom: "pool1"
72+
top: "norm1"
73+
lrn_param {
74+
local_size: 3
75+
alpha: 5e-05
76+
beta: 0.75
77+
norm_region: WITHIN_CHANNEL
78+
}
79+
}
80+
layer {
81+
name: "conv2"
82+
type: "Convolution"
83+
bottom: "norm1"
84+
top: "conv2"
85+
param {
86+
lr_mult: 1
87+
}
88+
param {
89+
lr_mult: 2
90+
}
91+
convolution_param {
92+
num_output: 32
93+
pad: 2
94+
kernel_size: 5
95+
stride: 1
96+
weight_filler {
97+
type: "gaussian"
98+
std: 0.01
99+
}
100+
bias_filler {
101+
type: "constant"
102+
}
103+
}
104+
}
105+
layer {
106+
name: "relu2"
107+
type: "ReLU"
108+
bottom: "conv2"
109+
top: "conv2"
110+
}
111+
layer {
112+
name: "pool2"
113+
type: "Pooling"
114+
bottom: "conv2"
115+
top: "pool2"
116+
pooling_param {
117+
pool: AVE
118+
kernel_size: 3
119+
stride: 2
120+
}
121+
}
122+
layer {
123+
name: "norm2"
124+
type: "LRN"
125+
bottom: "pool2"
126+
top: "norm2"
127+
lrn_param {
128+
local_size: 3
129+
alpha: 5e-05
130+
beta: 0.75
131+
norm_region: WITHIN_CHANNEL
132+
}
133+
}
134+
layer {
135+
name: "conv3"
136+
type: "Convolution"
137+
bottom: "norm2"
138+
top: "conv3"
139+
convolution_param {
140+
num_output: 64
141+
pad: 2
142+
kernel_size: 5
143+
stride: 1
144+
weight_filler {
145+
type: "gaussian"
146+
std: 0.01
147+
}
148+
bias_filler {
149+
type: "constant"
150+
}
151+
}
152+
}
153+
layer {
154+
name: "relu3"
155+
type: "ReLU"
156+
bottom: "conv3"
157+
top: "conv3"
158+
}
159+
layer {
160+
name: "pool3"
161+
type: "Pooling"
162+
bottom: "conv3"
163+
top: "pool3"
164+
pooling_param {
165+
pool: AVE
166+
kernel_size: 3
167+
stride: 2
168+
}
169+
}
170+
layer {
171+
name: "ip1"
172+
type: "InnerProduct"
173+
bottom: "pool3"
174+
top: "ip1"
175+
param {
176+
lr_mult: 1
177+
decay_mult: 250
178+
}
179+
param {
180+
lr_mult: 2
181+
decay_mult: 0
182+
}
183+
inner_product_param {
184+
num_output: 10
185+
weight_filler {
186+
type: "gaussian"
187+
std: 0.01
188+
}
189+
bias_filler {
190+
type: "constant"
191+
}
192+
}
193+
}
194+
layer {
195+
name: "accuracy"
196+
type: "Accuracy"
197+
bottom: "ip1"
198+
bottom: "label"
199+
top: "accuracy"
200+
include {
201+
phase: TEST
202+
}
203+
}
204+
layer {
205+
name: "loss"
206+
type: "SoftmaxWithLoss"
207+
bottom: "ip1"
208+
bottom: "label"
209+
top: "loss"
210+
}

libccaffe/CMakeLists.txt

+4-3
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../caffe/src/)
5656
## The files
5757

5858
file(GLOB Source GLOB "${CMAKE_CURRENT_SOURCE_DIR}/../caffe/src/caffe/*.cpp")
59+
file(GLOB SourceSolvers GLOB "${CMAKE_CURRENT_SOURCE_DIR}/../caffe/src/caffe/solvers/*.cpp")
5960
file(GLOB SourceUtil GLOB "${CMAKE_CURRENT_SOURCE_DIR}/../caffe/src/caffe/util/*.cpp")
6061
file(GLOB SourceGPUUtil GLOB "${CMAKE_CURRENT_SOURCE_DIR}/../caffe/src/caffe/util/*.cu")
6162
file(GLOB SourceLayers GLOB "${CMAKE_CURRENT_SOURCE_DIR}/../caffe/src/caffe/layers/*.cpp")
@@ -65,16 +66,16 @@ set(CUDA_NVCC_FLAGS "-gencode=arch=compute_20,code=\"sm_20,compute_20\" -gencode
6566

6667
# force generation of proto header
6768
add_library(proto STATIC ${CMAKE_CURRENT_SOURCE_DIR}/../caffe/include/caffe/proto/caffe.pb.h ${ProtoSources})
68-
set(Caffe_LINKER_LIBS ${OpenCV_LIBS} proto ${Caffe_LINKER_LIBS})
69+
set(Caffe_LINKER_LIBS proto ${Caffe_LINKER_LIBS})
6970

7071
if(CUDA_FOUND)
71-
cuda_add_library(ccaffe SHARED ${ProtoSources} ${ProtoHeaders} ${Source} ${SourceLayers} ${SourceGPULayers} ${SourceUtil} ${SourceGPUUtil} scalacaffe.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../caffe/include/caffe/proto/caffe.pb.h)
72+
cuda_add_library(ccaffe SHARED ${ProtoSources} ${ProtoHeaders} ${Source} ${SourceSolvers} ${SourceLayers} ${SourceGPULayers} ${SourceUtil} ${SourceGPUUtil} ccaffe.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../caffe/include/caffe/proto/caffe.pb.h)
7273

7374
cuda_add_cublas_to_target(ccaffe)
7475

7576
target_link_libraries(ccaffe ${PROTOBUF_LIBRARY} ${Caffe_LINKER_LIBS} ${CUDA_LIBRARIES} ${CUDA_curand_LIBRARY})
7677
else(CUDA_FOUND)
77-
add_library(ccaffe SHARED ${ProtoSources} ${ProtoHeaders} ${Source} ${SourceLayers} ${SourceUtil} ccaffe.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../caffe/include/caffe/proto/caffe.pb.h)
78+
add_library(ccaffe SHARED ${ProtoSources} ${ProtoHeaders} ${Source} ${SourceSolvers} ${SourceLayers} ${SourceUtil} ccaffe.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../caffe/include/caffe/proto/caffe.pb.h)
7879

7980
target_link_libraries(ccaffe ${PROTOBUF_LIBRARY} ${Caffe_LINKER_LIBS})
8081
endif(CUDA_FOUND)

libccaffe/ccaffe.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "caffe/common.hpp"
77
#include <string>
88
#include <boost/shared_ptr.hpp>
9+
#include <unistd.h>
910

1011
#include <glog/logging.h>
1112

@@ -26,6 +27,10 @@ void init_logging(const char* log_filename, int log_verbosity) {
2627
google::SetStderrLogging(log_verbosity);
2728
}
2829

30+
void set_basepath(const char* path) {
31+
chdir(path);
32+
}
33+
2934
int get_int_size() {
3035
return sizeof(int);
3136
}
@@ -162,7 +167,7 @@ void solver_test(caffenet_state* state, int num_steps) {
162167
state->solver->TestAndStoreResult(0, num_steps, state->test_score);
163168
}
164169

165-
DTYPE solver_get_test_score(caffenet_state* state, int accuracy_idx) {
170+
DTYPE get_test_score(caffenet_state* state, int accuracy_idx) {
166171
assert(0 <= accuracy_idx && accuracy_idx < state->test_score->size());
167172
return (*(state->test_score))[accuracy_idx];
168173
}

libccaffe/ccaffe.h

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ extern "C" {
1212
// initialize glog
1313
void init_logging(const char* log_filename, int log_verbosity);
1414

15+
void set_basepath(const char* path);
16+
1517
int get_int_size(); // get number of bytes for native int
1618
int get_dtype_size(); // get number of bytes for DTYPE
1719

project/plugins.sbt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import org.scalatest._
2+
import org.apache.spark.sparknet.loaders.CifarLoader
3+
import org.apache.spark.sparknet.CaffeLibrary
4+
import com.sun.jna.Pointer
5+
6+
// for this test to work, $SPARKNET_HOME/caffe should be the caffe root directory
7+
// and you need to run $SPARKNET_HOME/caffe/data/cifar10/get_cifar10.sh
8+
class CifarSpec extends FlatSpec {
9+
"A Cifar net" should "get chance digits right on randomly initialized net" in {
10+
val sparkNetHome = sys.env("SPARKNET_HOME")
11+
val loader = new CifarLoader(sparkNetHome + "/caffe/data/cifar10/")
12+
13+
System.load(sparkNetHome + "/build/libccaffe.so")
14+
val caffeLib = CaffeLibrary.INSTANCE
15+
16+
caffeLib.set_basepath(sparkNetHome + "/caffe/")
17+
val net = caffeLib.make_solver_from_prototxt(sparkNetHome + "/caffe/examples/cifar10/cifar10_full_java_solver.prototxt")
18+
19+
val dtypeSize = caffeLib.get_dtype_size()
20+
val intSize = caffeLib.get_int_size()
21+
22+
def makeImageCallback(images: Array[Array[Float]]) : CaffeLibrary.java_callback_t = {
23+
return new CaffeLibrary.java_callback_t() {
24+
var currImage = 0
25+
def invoke(data: Pointer, batch_size: Int, num_dims: Int, shape: Pointer) {
26+
var size = 1
27+
for(i <- 0 to num_dims-1) {
28+
val dim = shape.getInt(i * intSize)
29+
size *= dim
30+
}
31+
for(j <- 0 to batch_size-1) {
32+
assert(size == images(currImage).length)
33+
for(i <- 0 to size-1) {
34+
data.setFloat((j * size + i) * dtypeSize, images(currImage)(i))
35+
}
36+
currImage += 1
37+
if(currImage == images.length) {
38+
currImage = 0
39+
}
40+
}
41+
}
42+
};
43+
}
44+
45+
def makeLabelCallback(labels: Array[Float]) : CaffeLibrary.java_callback_t = {
46+
return new CaffeLibrary.java_callback_t() {
47+
var currImage = 0
48+
def invoke(data: Pointer, batch_size: Int, num_dims: Int, shape: Pointer) {
49+
for(j <- 0 to batch_size-1) {
50+
assert(shape.getInt(0) == 1)
51+
data.setFloat(j * dtypeSize, labels(currImage))
52+
currImage += 1
53+
if(currImage == labels.length) {
54+
currImage = 0
55+
}
56+
}
57+
}
58+
};
59+
}
60+
61+
val loadTrainImageFn = makeImageCallback(loader.trainImages)
62+
val loadTrainLabelFn = makeLabelCallback(loader.trainLabels)
63+
caffeLib.set_train_data_callback(net, 0, loadTrainImageFn)
64+
caffeLib.set_train_data_callback(net, 1, loadTrainLabelFn)
65+
66+
val loadTestImageFn = makeImageCallback(loader.testImages)
67+
val loadTestLabelFn = makeLabelCallback(loader.testLabels)
68+
caffeLib.set_test_data_callback(net, 0, loadTestImageFn)
69+
caffeLib.set_test_data_callback(net, 1, loadTestLabelFn)
70+
71+
caffeLib.solver_test(net, 10) // TODO: shouldn't be hard coded
72+
73+
val numTestScores = caffeLib.num_test_scores(net)
74+
75+
val testScores = new Array[Float](numTestScores)
76+
77+
// perform test on random net
78+
for (i <- 0 to numTestScores - 1) {
79+
testScores(i) = caffeLib.get_test_score(net, i) * 100 // TODO: this batch size shouldn't be hard coded
80+
}
81+
82+
assert(70.0 <= testScores(0) && testScores(0) <= 130.0)
83+
}
84+
}

0 commit comments

Comments
 (0)