├── fig.jpg
├── chainer
├── mean-std-20170714.csv
├── funcReadLocations.py
├── test.sh
├── train.sh
├── forwardFunc_IshiiModel.py
├── funcLoadBatcFiles.py
├── megasolar_model_ishii.py
├── funcMakeNormFiles.py
├── trainMegaSolarData_IshiiModel_LoadAll.py
└── testMegaSolarData_IshiiModel_LoadAll_outIoU.py
├── torch
├── train_size16_meanstd.csv
├── train-cnn.sh
├── convertGPUToCPU.lua
├── test-cnn.sh
├── test_hdf5_list.txt
├── ComputeCM.py
├── test-cnn-cpu.lua
└── train-cnn.lua
├── LICENSE.md
└── README.md
/fig.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gistairc/MUSIC4P3/HEAD/fig.jpg
--------------------------------------------------------------------------------
/chainer/mean-std-20170714.csv:
--------------------------------------------------------------------------------
1 | 10523.2,2727.81
2 | 9618.03,2866.96
3 | 8597.75,2906.9
4 | 7786.07,3190.72
5 | 12724.9,6492.82
6 | 9153.44,3817.24
7 | 7339.11,2623.78
8 |
--------------------------------------------------------------------------------
/chainer/funcReadLocations.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | def ReadLocations(filename):
4 | with open(filename, "r") as ins:
5 | array = []
6 | for line in ins:
7 | array.append(line.rstrip('\n'))
8 | return array
9 |
--------------------------------------------------------------------------------
/torch/train_size16_meanstd.csv:
--------------------------------------------------------------------------------
1 | 10523.166260972,2727.812099227
2 | 9618.029140872,2866.9551326075
3 | 8597.7603006359,2906.9052381504
4 | 7786.0726436288,3190.7179589405
5 | 12724.932743973,6492.823863544
6 | 9153.4478082235,3817.2383888866
7 | 7339.119508997,2623.7799687889
8 |
--------------------------------------------------------------------------------
/chainer/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | NORM="mean-std"
4 |
5 | python testMegaSolarData_IshiiModel_LoadAll_outIoU.py \
6 | --filelocationspath "./resource" \
7 | --positivefilename "locationPositiveTestData.txt" \
8 | --negativefilename "locationNegativeTestData.txt" \
9 | --meanstdfilepath ${NORM}".csv" \
10 | --modelFileNamePath "./result/newmegasolarCNN_NS_72720_"
11 |
--------------------------------------------------------------------------------
/chainer/train.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | NORM="mean-std"
4 |
5 | mkdir result
6 |
7 | python funcMakeNormFiles.py \
8 | --filelocationspath "./resource" \
9 | --positivefilename "locationPositiveTrainData.txt" \
10 | --negativefilename "locationNegativeTrainData.txt" \
11 | --meanstdfilepath ${NORM}
12 |
13 | python trainMegaSolarData_IshiiModel_LoadAll.py \
14 | --filelocationspath "./resource" \
15 | --positivefilename "locationPositiveTrainData.txt" \
16 | --negativefilename "locationNegativeTrainData.txt" \
17 | --meanstdfilepath ${NORM}".csv" \
18 | --modelFileNamePath "./result/newmegasolarCNN_NS_72720_"
19 |
--------------------------------------------------------------------------------
/torch/train-cnn.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | GPU=1
4 |
5 | MODEL=ishiinet
6 | echo ${MODEL}
7 |
8 | for i in 15
9 | do
10 | th train-cnn.lua \
11 | -save ${MODEL}_p1n${i} \
12 | -gpu ${GPU} \
13 | -trainDataPath ./resource/train \
14 | -testDataPath ./resource/val \
15 | -batchNorm \
16 | -dropout \
17 | -batchSize 128\
18 | -batchSizeNM 32 \
19 | -maxEpoch 3000 \
20 | -negativeRatio ${i} \
21 | -saveInterval 10 \
22 | -optimization SGD \
23 | -trainnorm ${MODEL}_meanstd.csv \
24 | | tee log_${MODEL}_p1n${i}.txt
25 | done
26 |
--------------------------------------------------------------------------------
/chainer/forwardFunc_IshiiModel.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import chainer
3 | from chainer import Function, gradient_check, Variable, optimizers, utils
4 | from chainer import Link, Chain, ChainList
5 |
6 | import chainer.functions as F
7 | import chainer.links as L
8 |
9 | def forward(model,x):
10 |
11 | h = model.conv1(x)
12 | h = F.relu(h)
13 | h = model.bn1(h, test=True)
14 |
15 | h = model.conv2(h)
16 | h = F.relu(h)
17 | h = model.bn2(h, test=True)
18 |
19 | h = model.conv3(h)
20 | h = F.relu(h)
21 | h = model.bn2(h, test=True)
22 |
23 | h = model.fcl(h)
24 | y = F.softmax(h)
25 |
26 | model.pred = y
27 |
28 | return y
29 |
--------------------------------------------------------------------------------
/chainer/funcLoadBatcFiles.py:
--------------------------------------------------------------------------------
1 | import tifffile as tiff
2 | import numpy as np
3 |
4 |
5 |
6 | def loadbatchfiles(filelist,indexlist,dataMean,dataStd):
7 |
8 | inputchanel = 7
9 | rowsize = 16
10 | colsize = 16
11 |
12 | print dataMean
13 | print dataStd
14 |
15 | statusPrint = False
16 | NumData = len(indexlist)
17 | if NumData > 10000:
18 | statusPrint = True
19 | print('Number of data to be loaded %d' % NumData)
20 |
21 | x_data = np.float32(np.zeros([len(indexlist),inputchanel,rowsize,colsize]))
22 |
23 | for item in range(NumData):
24 | arr = tiff.imread(filelist[indexlist[item]])
25 | for channel in range(inputchanel):
26 | x_data[item,channel] = np.float32( np.float32(arr[channel]) - np.float32(dataMean[channel] ) / np.float32(dataStd[channel]))
27 | if statusPrint:
28 | if item%10000 == 0:
29 | print('%d of %d data loading complete' % (item,NumData))
30 |
31 | return x_data
32 |
33 |
34 |
--------------------------------------------------------------------------------
/torch/convertGPUToCPU.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | require 'cunn'
4 | require 'optim'
5 | require 'image'
6 | require 'hdf5'
7 | require 'cutorch'
8 |
9 | --
10 | cmd = torch.CmdLine()
11 | cmd:text()
12 | cmd:text('CNN TEST')
13 | cmd:text()
14 | cmd:text('Options:')
15 | cmd:option('-network', '', 'reload pretrained network')
16 | cmd:option('-outputNetwork', '', 'name of output network')
17 | cmd:option('-gpu', 1, 'id of gpu to use')
18 | cmd:text()
19 | opt = cmd:parse(arg)
20 | --
21 |
22 |
23 | cutorch.setDevice(opt.gpu)
24 |
25 | model = torch.load(opt.network)
26 | networkCpu = string.split(opt.network, '.net')[1]..'_cpu.net'
27 |
28 | if torch.typename(model.modules[1].weight) == 'torch.CudaTensor' then
29 | print('Converted network to CPU-mode.')
30 | if opt.outputNetwork == '' then
31 | torch.save(networkCpu, model:double())
32 | else
33 | torch.save(opt.outputNetwork, model:double())
34 | end
35 | else
36 | print('Input network is already CPU-mode.')
37 | end
38 |
--------------------------------------------------------------------------------
/torch/test-cnn.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | LIST=test_hdf5_list.txt
4 |
5 |
6 | DATA_PATH='./resource'
7 | MODE=ishiinet
8 |
9 | NETWORK_PATH=./${MODEL}_p1n15
10 | NETWORK='cnn_nm_ep2000.net'
11 | OUTPUT_NETWORK='cnn_nm_ep2000_cpu.net'
12 |
13 | th convertGPUToCPU.lua \
14 | -network ${NETWORK_PATH}/${NETWORK} \
15 | -outputNetwork ${NETWORK_PATH}/${OUTPUT_NETWORK} \
16 | -gpu 1
17 |
18 | for THRESHOLD in 0.999
19 | do
20 | cat ${LIST} | while read HDF5
21 | do
22 | th test-cnn-cpu.lua \
23 | -testDataPath ${DATA_PATH}/test/${HDF5} \
24 | -testBatchSize 4000 \
25 | -network ${NETWORK_PATH}/${OUTPUT_NETWORK} \
26 | -threshold ${THRESHOLD} \
27 | -meanstd_file ${MODEL}_meanstd.csv \
28 | | tee ${NETWORK_PATH}/log_th${THRESHOLD}_${HDF5}.txt
29 | done
30 |
31 | for i in ${NETWORK_PATH}/log_th${THRESHOLD}_*${MODE}.hdf5.txt
32 | do
33 | echo ${i}
34 | cat ${i} >> ${NETWORK_PATH}/${MODE}_th${THRESHOLD}_all.txt
35 | done
36 |
37 | python ComputeCM.py \
38 | --log_file ${NETWORK_PATH}/${MODE}_th${THRESHOLD}_all.txt \
39 | > ${NETWORK_PATH}/${MODE}_th${THRESHOLD}_all_cm.txt
40 |
41 | done
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 |
MUSIC for P3 dataset Terms of Use
2 | October 20 , 2017
3 |
4 | The users of the MUSIC for P3 dataset must abide by the following Terms of Use:
5 |
6 |
7 | 1. When publishing results that use this dataset, the users shall comply with the following matters:
8 | * The users must cite the URL of the MUSIC for P3 dataset (i.e., https://github.com/gistairc/MUSIC4P3) in their publication.
9 |
10 |
11 | 2. By downloading the dataset, the users are regarded as having agreed to the Terms of Use.
12 |
13 | 3. The software is provided "as is", without warranty of any kind, express or implined, including but not limited to the warranties of merchantabilty, fitness for a particular purpose and noninfringement.
14 | In no event shall the authors or copyright holders be liable for any claim. damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.
15 |
16 | MUSIC for P3 dataset is based on results obtained from a project commissioned by the New Energy and Industrial Technology Development Organization (NEDO).
17 |
18 | National Institute of Advanced Industrial Science and Technology
19 |
20 | Artificial Intelligence Research Center, Japan
21 |
--------------------------------------------------------------------------------
/chainer/megasolar_model_ishii.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import chainer
3 | from chainer import Function, gradient_check, Variable, optimizers, utils
4 | from chainer import Link, Chain, ChainList
5 |
6 | import chainer.functions as F
7 | import chainer.links as L
8 |
9 | class MegaSolarBN(chainer.Chain):
10 | insize = 16
11 | def __init__(self):
12 | super(MegaSolarBN, self).__init__(
13 | conv1=L.Convolution2D(7, 32, 3, stride=1,pad=0),
14 | bn1=L.BatchNormalization(32),
15 | conv2=L.Convolution2D(32, 32, 3, stride=1,pad=0),
16 | bn2=L.BatchNormalization(32),
17 | conv3=L.Convolution2D(32, 32, 3, stride=1,pad=0),
18 | bn3=L.BatchNormalization(32),
19 | fcl=L.Linear(32*10*10, 2),
20 | )
21 | self.train = True
22 |
23 | def __call__(self, x,t):
24 | h = self.conv1(x)
25 | h = F.relu(h)
26 | h = self.bn1(h, test=not self.train)
27 |
28 | h = self.conv2(h)
29 | h = F.relu(h)
30 | h = self.bn2(h, test=not self.train)
31 |
32 | h = self.conv3(h)
33 | h = F.relu(h)
34 | h = self.bn3(h, test=not self.train)
35 |
36 | h = F.dropout(h, train=self.train)
37 |
38 | h = self.fcl(h)
39 |
40 |
41 | self.pred = F.softmax(h)
42 | self.loss = F.softmax_cross_entropy(h, t)
43 | self.accuracy = F.accuracy(self.pred, t)
44 | if self.train:
45 | return self.loss
46 | else:
47 | return self.pred
48 |
49 |
50 |
--------------------------------------------------------------------------------
/torch/test_hdf5_list.txt:
--------------------------------------------------------------------------------
1 | LC81050302016143LGN00_P12_N40385.hdf5
2 | LC81090342016155LGN00_P64_N40079.hdf5
3 | LC81060292016182LGN00_P22_N40354.hdf5
4 | LC81090352016139LGN00_P274_N39940.hdf5
5 | LC81060302016134LGN00_P51_N40247.hdf5
6 | LC81090362016139LGN00_P449_N39634.hdf5
7 | LC81070292016189LGN00_P24_N40458.hdf5
8 | LC81100342016290LGN00_P7_N40240.hdf5
9 | LC81070302015266LGN00_P210_N40187.hdf5
10 | LC81100352016082LGN00_P77_N40246.hdf5
11 | LC81070312016237LGN00_P20_N40400.hdf5
12 | LC81100362016082LGN00_P529_N39523.hdf5
13 | LC81070322016221LGN00_P60_N40343.hdf5
14 | LC81100372016082LGN00_P31_N40307.hdf5
15 | LC81070332015346LGN00_P95_N40248.hdf5
16 | LC81110352015294LGN00_P72_N40332.hdf5
17 | LC81070342016077LGN00_P244_N39991.hdf5
18 | LC81110362016121LGN00_P251_N39996.hdf5
19 | LC81070352016077LGN00_P862_N38979.hdf5
20 | LC81110372016121LGN00_P42_N40336.hdf5
21 | LC81070362016189LGN00_P340_N39934.hdf5
22 | LC81120362016144LGN00_P162_N40098.hdf5
23 | LC81080302015145LGN00_P197_N40182.hdf5
24 | LC81120372016224LGN00_P173_N39953.hdf5
25 | LC81080312015193LGN00_P25_N40387.hdf5
26 | LC81120382016080LGN00_P95_N40020.hdf5
27 | LC81080322016116LGN00_P57_N40330.hdf5
28 | LC81130372016151LGN00_P213_N39808.hdf5
29 | LC81080332016244LGN00_P43_N40330.hdf5
30 | LC81130382015308LGN00_P51_N39994.hdf5
31 | LC81080342016244LGN00_P166_N40079.hdf5
32 | LC81130422015260LGN00_P40_N40142.hdf5
33 | LC81080352016004LGN00_P753_N39254.hdf5
34 | LC81140372016062LGN00_P2_N40385.hdf5
35 | LC81080362016004LGN00_P238_N40058.hdf5
36 |
--------------------------------------------------------------------------------
/torch/ComputeCM.py:
--------------------------------------------------------------------------------
1 |
2 | # coding: utf-8
3 |
4 | import string
5 | import numpy as np
6 | import argparse
7 |
8 | parser = argparse.ArgumentParser(description='')
9 | parser.add_argument('--log_file', default='')
10 | args = parser.parse_args()
11 |
12 |
13 | f = open(args.log_file, 'r')
14 | str_list = []
15 | for line in f:
16 | str_list.append(line)
17 | f.close()
18 |
19 |
20 | def get_cm_value(str_line):
21 | tmp_str = []
22 | for i in range(len(str_line)):
23 | if str_line[i] != '':
24 | tmp_str.append(str_line[i])
25 |
26 | return int(tmp_str[1]), int(string.split(tmp_str[2], ']')[0])
27 |
28 | result_list = []
29 | for i in range(len(str_list)):
30 | tmp_line = string.split(str_list[i],' ')
31 | if ('test' in tmp_line) and ('all' in tmp_line):
32 | result_list.append([i,
33 | get_cm_value(string.split(str_list[i+2])),
34 | get_cm_value(string.split(str_list[i+3]))])
35 |
36 | confusion = [[0,0],[0,0]]
37 |
38 | IOU_list = []
39 | for i in range(len(result_list)):
40 | tp = result_list[i][1][0]
41 | fn = result_list[i][1][1]
42 | fp = result_list[i][2][0]
43 | tn = result_list[i][2][1]
44 | for j in [0,1]:
45 | for k in [0,1]:
46 | confusion[j][k] = confusion[j][k] + result_list[i][j+1][k]
47 |
48 | iou = float(confusion[0][0])/float(confusion[0][0]+confusion[0][1]+confusion[1][0])
49 | recall = float(confusion[0][0])/float(confusion[0][0]+confusion[0][1])
50 | precision = float(confusion[0][0])/float(confusion[0][0]+confusion[1][0])
51 |
52 |
53 | print 'Confusion Matrix:'
54 | print confusion
55 |
56 | print 'IOU:'+str(iou)
57 | print 'Precision:'+str(precision)
58 | print 'Recall:'+str(recall)
59 |
60 |
--------------------------------------------------------------------------------
/chainer/funcMakeNormFiles.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #bin/python
3 | import tifffile as tiff
4 | import numpy as np
5 |
6 | import sys
7 | import argparse
8 |
9 | import funcReadLocations as readtxt
10 |
11 | import sys
12 |
13 | parser = argparse.ArgumentParser(description='MakeNormariztionFiles(mean std)')
14 | parser.add_argument('--filelocationspath', default='resource')
15 | parser.add_argument('--positivefilename', default='locationPositiveTrainData.txt')
16 | parser.add_argument('--negativefilename', default='locationNegativeTrainData.txt')
17 | parser.add_argument('--meanstdfilepath', default='meanstd')
18 | args = parser.parse_args()
19 |
20 | def mathdatameanstd(pfile,pindex,nfile,nindex,inputchanel):
21 |
22 |
23 | image=[]
24 | for channel in range(inputchanel):
25 | band=[]
26 | image.append(band)
27 | pNum= len(pindex)
28 | nNum= len(nindex)
29 | for NumData in pNum,nNum:
30 | if NumData is pNum:
31 | filelist = pfile
32 | index = pindex
33 | elif NumData is nNum:
34 | filelist = nfile
35 | index = nindex
36 | for item in range(NumData):
37 | arr = tiff.imread(filelist[index[item]])
38 | for channel in range(inputchanel):
39 | image[channel].append(np.float32(arr[channel]))
40 |
41 | mean=[]
42 | std=[]
43 | for channel in range(inputchanel):
44 | data = np.array(image[channel])
45 | mean.append(np.mean(data))
46 | std.append(np.std(data))
47 |
48 | return mean,std
49 |
50 | if __name__ == "__main__":
51 |
52 | inputchanel=7
53 |
54 | pfilename = args.filelocationspath + "/"+args.positivefilename
55 | trainPosLocations = readtxt.ReadLocations(pfilename);
56 | nfilename = args.filelocationspath + "/"+args.negativefilename
57 | trainNegLocations = readtxt.ReadLocations(nfilename);
58 |
59 | negdatasize = len(trainNegLocations)
60 | posdatasize = len(trainPosLocations)
61 |
62 | mean,std = mathdatameanstd(trainPosLocations,range(posdatasize),trainNegLocations,range(negdatasize),inputchanel)
63 |
64 | file1 = open(str(args.meanstdfilepath)+".csv",'a')
65 |
66 | for channel in range(inputchanel):
67 | file1.write(str(mean[channel])+","+str(std[channel])+'\n')
68 | file1.close()
69 |
70 |
71 |
--------------------------------------------------------------------------------
/chainer/trainMegaSolarData_IshiiModel_LoadAll.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #bin/python
3 |
4 | import numpy as np
5 | import chainer
6 | from chainer import Function, gradient_check, Variable, optimizers, utils
7 | from chainer import Link, Chain, ChainList
8 | from chainer import serializers
9 | from chainer import cuda
10 | from chainer import computational_graph
11 |
12 |
13 | import funcReadLocations as readtxt
14 | import megasolar_model_ishii as cnnmodel
15 | import funcLoadBatcFiles as bl
16 |
17 | import chainer.functions as F
18 | import chainer.links as L
19 |
20 | import os
21 | import csv
22 | import argparse
23 |
24 |
25 |
26 | parser = argparse.ArgumentParser(description='train')
27 | parser.add_argument('--filelocationspath', default='resource')
28 | parser.add_argument('--trainDataPath', default='train')
29 | parser.add_argument('--positivefilename', default='locationPositiveTrainData.txt')
30 | parser.add_argument('--negativefilename', default='locationNegativeTrainData.txt')
31 | parser.add_argument('--meanstdfilepath', default='meanstd')
32 | parser.add_argument('--modelFileNamePath', default='')
33 | args = parser.parse_args()
34 |
35 | def get_coordinate_from_csv(csv_file):
36 | print csv_file
37 | f = open(str(csv_file),'r')
38 | file_list = csv.reader(f)
39 | cor_list = []
40 | mean=[]
41 | std=[]
42 | for row in file_list:
43 | cor_list.append(row)
44 | f.close()
45 | for i in range(len(cor_list)):
46 | mean.append(np.float32(cor_list[i][0]))
47 | std.append(np.float32(cor_list[i][1]))
48 | return mean,std
49 |
50 | def trainMegaSolarCNN():
51 |
52 |
53 |
54 | # paths in the local machine
55 | filelocationspath = args.filelocationspath
56 |
57 |
58 | filename = filelocationspath +"/"+args.positivefilename
59 | trainPosLocations = readtxt.ReadLocations(filename);
60 | filename = filelocationspath +"/"+args.negativefilename
61 | trainNegLocations = readtxt.ReadLocations(filename);
62 |
63 |
64 | model = cnnmodel.MegaSolarBN()
65 | optimizer = optimizers.MomentumSGD(lr=0.001, momentum=0.9)
66 | optimizer.setup(model)
67 | model.to_gpu()
68 |
69 | negbatchsize = 120
70 | posbatchsize = 8
71 | negdatasize = len(trainNegLocations)
72 | posdatasize = len(trainPosLocations)
73 | datamean=[]
74 | datastd=[]
75 | datamean,datastd=get_coordinate_from_csv(args.meanstdfilepath)
76 |
77 | x_train_all_positive = bl.loadbatchfiles(trainPosLocations,range(posdatasize),datamean,datastd)
78 | x_train_all_negative = bl.loadbatchfiles(trainNegLocations,range(negdatasize),datamean,datastd)
79 |
80 |
81 | NumNegTrainData = 72720
82 |
83 | print('Number of Negative samples %d' % (negdatasize))
84 | print('Number of Positive samples %d' % (posdatasize))
85 | print('----------------------------------------------------------')
86 | print('Number of Negative samples at each epoch %d' % (NumNegTrainData))
87 | print('Number of Positive samples at each epoch %d' % (posdatasize))
88 | print('----------------------------------------------------------')
89 |
90 | batchlooplen = len(range(0, NumNegTrainData, negbatchsize));
91 | datasize = batchlooplen*(posbatchsize+negbatchsize)
92 | NUMEPOCH = 5000
93 | for epoch in range(NUMEPOCH):
94 | print('epoch %d' % epoch)
95 | sum_loss = 0
96 | sum_accuracy = 0
97 | mean_loss = np.zeros(NUMEPOCH)
98 | mean_accuracy = np.zeros(NUMEPOCH)
99 | negindexes = np.random.permutation(negdatasize)
100 | posindexes = np.random.permutation(posdatasize)
101 | posindexes = np.tile(posindexes,(10*8))
102 | for i in range(0, NumNegTrainData, negbatchsize):
103 |
104 |
105 | x_train_positive = x_train_all_positive[posindexes[(i/negbatchsize)*posbatchsize:(i/negbatchsize)*posbatchsize+posbatchsize]]
106 | y_train_positive = np.int32(np.ones(posbatchsize))
107 |
108 | x_train_negative = x_train_all_negative[negindexes[i:i+negbatchsize]]
109 | y_train_negative = np.int32(np.zeros(negbatchsize))
110 |
111 | x_train = np.append(x_train_positive,x_train_negative,axis=0)
112 | y_train = np.append(y_train_positive,y_train_negative)
113 |
114 | indexes = np.random.permutation(posbatchsize+negbatchsize)
115 | x = Variable(cuda.to_gpu(x_train[indexes]))
116 | t = Variable(cuda.to_gpu(y_train[indexes]))
117 |
118 |
119 | optimizer.update(model,x,t)
120 | loss = model(x, t)
121 |
122 | sum_loss += loss.data * (posbatchsize+negbatchsize)
123 | sum_accuracy += model.accuracy.data * (posbatchsize+negbatchsize)
124 |
125 | print "EPOCH MEAN TRAIN LOSS and ACCURACY VALUES: "
126 | mean_loss[epoch] = sum_loss / datasize
127 | mean_accuracy[epoch] = sum_accuracy / datasize
128 | print mean_loss[epoch]
129 | print mean_accuracy[epoch]
130 |
131 | modelFileName = str(args.modelFileNamePath)+str(epoch)+"_iteration.model"
132 | stateFileName = str(args.modelFileNamePath)+str(epoch)+"_iteration.state"
133 | serializers.save_npz(modelFileName, model)
134 | serializers.save_npz(stateFileName, optimizer)
135 |
136 | return model,optimizer,loss,mean_loss,mean_accuracy
137 |
138 | if __name__ == "__main__":
139 | model,optimizer,loss,mean_loss,mean_accuracy=trainMegaSolarCNN()
140 |
--------------------------------------------------------------------------------
/chainer/testMegaSolarData_IshiiModel_LoadAll_outIoU.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #bin/python
3 |
4 | import numpy as np
5 | import chainer
6 | from chainer import Function, gradient_check, Variable, optimizers, utils
7 | from chainer import Link, Chain, ChainList
8 | from chainer import serializers
9 | from chainer import cuda
10 | from chainer import computational_graph
11 | import libtiff
12 |
13 | import funcReadLocations as readtxt
14 | import megasolar_model_ishii as cnnmodel
15 | import forwardFunc_IshiiModel as cnnForward
16 | import funcLoadBatcFiles as bl
17 |
18 |
19 | import chainer.functions as F
20 | import chainer.links as L
21 |
22 | import os
23 | import csv
24 | import argparse
25 |
26 |
27 | parser = argparse.ArgumentParser(description='train')
28 | parser.add_argument('--filelocationspath', default='resource')
29 | parser.add_argument('--testDataPath', default='test')
30 | parser.add_argument('--positivefilename', default='locationPositiveTrainData.txt')
31 | parser.add_argument('--negativefilename', default='locationNegativeTrainData.txt')
32 | parser.add_argument('--meanstdfilepath', default='meanstd')
33 | parser.add_argument('--modelFileNamePath', default='')
34 | args = parser.parse_args()
35 |
36 | def get_coordinate_from_csv(csv_file):
37 | print csv_file
38 | f = open(str(csv_file),'r')
39 | file_list = csv.reader(f)
40 | cor_list = []
41 | mean=[]
42 | std=[]
43 | for row in file_list:
44 | cor_list.append(row)
45 | f.close()
46 | for i in range(len(cor_list)):
47 | mean.append(np.float32(cor_list[i][0]))
48 | std.append(np.float32(cor_list[i][1]))
49 | return mean,std
50 |
51 | def testMegaSolarCNN():
52 |
53 |
54 |
55 | # paths in the local machine
56 | filelocationspath = args.filelocationspath
57 |
58 | filename = filelocationspath +"/"+args.positivefilename
59 | testPosLocations = readtxt.ReadLocations(filename);
60 | filename = filelocationspath +"/"+args.negativefilename
61 | testNegLocations = readtxt.ReadLocations(filename);
62 |
63 |
64 | negbatchsize = 500
65 | posdatasize = len(testPosLocations)
66 |
67 | negdatasize_part1 = len(testNegLocations) - len(testNegLocations)%negbatchsize
68 | negdatasize = len(testNegLocations)
69 |
70 |
71 | datamean=[]
72 | datastd=[]
73 | datamean,datastd=get_coordinate_from_csv(args.meanstdfilepath)
74 |
75 |
76 | x_test_all_positive = bl.loadbatchfiles(testPosLocations,range(posdatasize),datamean,datastd)
77 | x_test_all_negative = bl.loadbatchfiles(testNegLocations,range(negdatasize),datamean,datastd)
78 |
79 | print('Number of Negative samples %d' % (negdatasize))
80 | print('Number of Positive samples %d' % (posdatasize))
81 | print('----------------------------------------------------------')
82 |
83 |
84 | model = cnnmodel.MegaSolarBN()
85 |
86 | MAX_EPOCH = 5000
87 | accuracy_pos = []
88 | accuracy_neg = []
89 |
90 | iou_all = []
91 |
92 | my_pos_thresh = 0.999
93 |
94 | for epoch in range(0,MAX_EPOCH, 1):
95 | print('EPOCH %d' % epoch)
96 | modelFileNamePath=str(args.modelFileNamePath)
97 | modelFileName = modelFileNamePath+str(epoch)+"_iteration.model"
98 | serializers.load_npz(modelFileName, model)
99 | model.to_gpu()
100 |
101 | model.train = False
102 | print "___________________________________________"
103 | print "Epoch MEAN TEST ACCURACY Positive Samples: "
104 | x_test_positive = x_test_all_positive
105 | y_test_positive = np.int32(np.ones(len(testPosLocations)))
106 | x = Variable(cuda.to_gpu(x_test_positive))
107 | t = Variable(cuda.to_gpu(y_test_positive))
108 | respos = cnnForward.forward(model,x)
109 | cnn_prediction = np.float32(cuda.to_cpu(respos.data[:,1]) > my_pos_thresh)
110 | cnn_acc = cnn_prediction.sum()/posdatasize
111 | accuracy_pos = np.append(accuracy_pos,cnn_acc)
112 | print cnn_acc
113 | x_pos_test = x
114 | t_pos_test = t
115 | print "___________________________________________"
116 | print "Epoch MEAN TEST ACCURACY Negative Samples: "
117 |
118 | sum_accuracy = 0
119 | for ti in range(0,negdatasize_part1, negbatchsize):
120 |
121 | x_test_negative = x_test_all_negative[ti:ti+negbatchsize]
122 | y_test_negative = np.int32(np.zeros(negbatchsize))
123 | x = Variable(cuda.to_gpu(x_test_negative))
124 | t = Variable(cuda.to_gpu(y_test_negative))
125 | resneg = cnnForward.forward(model,x)
126 | cnn_prediction = np.float32(cuda.to_cpu(resneg.data[:,1]) <= my_pos_thresh)
127 | cnn_acc = cnn_prediction.sum()/negbatchsize
128 | sum_accuracy += cnn_acc * (negbatchsize)
129 |
130 |
131 | x_test_negative = x_test_all_negative[negdatasize_part1:negdatasize]
132 | y_test_negative = np.int32(np.zeros(len(range(negdatasize_part1,negdatasize))))
133 | x = Variable(cuda.to_gpu(x_test_negative))
134 | t = Variable(cuda.to_gpu(y_test_negative))
135 | resneg = cnnForward.forward(model,x)
136 | cnn_prediction = np.float32(cuda.to_cpu(resneg.data[:,1]) <= my_pos_thresh)
137 | cnn_acc = cnn_prediction.sum()/len(y_test_negative)
138 | sum_accuracy += cnn_acc * len(y_test_negative)
139 | test_negative_accuracy = sum_accuracy / negdatasize
140 |
141 | accuracy_neg = np.append(accuracy_neg,test_negative_accuracy)
142 |
143 | print test_negative_accuracy
144 | x_neg_test = x
145 | t_neg_test = t
146 | TP = np.round(accuracy_pos[-1]*posdatasize)
147 | FN = posdatasize - TP
148 | FP = np.round(negdatasize - accuracy_neg[-1]*negdatasize)
149 | TN = negdatasize - FP
150 |
151 | iou_all = np.append(iou_all,TP/(TP+FN+FP))
152 | print "___________________________________________"
153 | print "Epoch MEAN TEST Intersection over Union: "
154 | print iou_all[-1]
155 | print "TP:"+str(TP)+" FN:"+str(FN)
156 | print "FP:"+str(FP)+" TN:"+str(TN)
157 |
158 | return model, iou_all, x_pos_test, t_pos_test, x_neg_test, t_neg_test
159 |
160 | if __name__ == "__main__":
161 | model, iou_all, x_pos_test, t_pos_test, x_neg_test, t_neg_test=testMegaSolarCNN()
162 |
--------------------------------------------------------------------------------
/torch/test-cnn-cpu.lua:
--------------------------------------------------------------------------------
1 |
2 | require 'nn'
3 | require 'optim'
4 | require 'image'
5 | require 'hdf5'
6 | require 'torch'
7 | require 'lfs'
8 |
9 | --
10 | cmd = torch.CmdLine()
11 | cmd:text()
12 | cmd:text('CNN TEST')
13 | cmd:text()
14 | cmd:text('Options:')
15 | cmd:option('-network', '', 'reload pretrained network')
16 | cmd:option('-inputChannel', 7, 'nb of input channels')
17 | cmd:option('-testBatchSize', 10000, 'test batch size')
18 | cmd:option('-trainDataPath', '', 'path of hdf5 dataset for training')
19 | cmd:option('-testDataPath', '', 'path of hdf5 dataset for test')
20 | cmd:option('-threshold', 0.5, 'threshold of softmax')
21 | cmd:option('-meanstd_file', 'mean_stdv.csv', 'training mean and stdv data')
22 | cmd:text()
23 | opt = cmd:parse(arg)
24 | --
25 |
26 | model = torch.load(opt.network):double()
27 | criterion = nn.ClassNLLCriterion():double()
28 |
29 | function split(str, delim)
30 | -- Eliminate bad cases...
31 | if string.find(str, delim) == nil then
32 | return { str }
33 | end
34 |
35 | local result = {}
36 | local pat = "(.-)" .. delim .. "()"
37 | local lastPos
38 | for part, pos in string.gfind(str, pat) do
39 | table.insert(result, part)
40 | lastPos = pos
41 | end
42 | table.insert(result, string.sub(str, lastPos))
43 | return result
44 | end
45 |
46 | file = io.open(opt.meanstd_file,"r")
47 | dataMean={}
48 | dataStd ={}
49 | for line in file:lines() do
50 | local datas = split(line, ",")
51 | table.insert(dataMean,datas[1])
52 | table.insert(dataStd,datas[2])
53 | end
54 |
55 | function getFileList(dirPath, ext)
56 | list = {}
57 | for file in lfs.dir(dirPath) do
58 | if string.find(file, ext..'$') then
59 | table.insert(list,file)
60 | end
61 | end
62 | return list
63 | end
64 |
65 | -- this matrix records the current confusion across classes
66 | classes = {'positive', 'negative'}
67 | confusion = optim.ConfusionMatrix(classes)
68 |
69 | probList = {}
70 | labelList = {}
71 |
72 |
73 | -- import test data
74 | if lfs.attributes(opt.testDataPath)['mode'] == 'file' then
75 | -- import training data
76 | print("Import test data...")
77 | print('Importing:'..opt.testDataPath)
78 | myFile = hdf5.open(opt.testDataPath,'r')
79 | testData = myFile:read(''):all()
80 | myFile:close()
81 | labelNum = testData.label:size()[1]
82 |
83 | for i = 1,labelNum do
84 | if testData.label[i] == 0 then
85 | testData.label[i] = 1
86 | else
87 | testData.label[i] = 2
88 | end
89 | end
90 |
91 | numAllTestData = labelNum
92 |
93 | testIdx = testData.idx
94 |
95 |
96 | else
97 | error("invalid dataset")
98 | end
99 |
100 | for t=1,opt.inputChannel do
101 | testData.data[{ {},t,{},{} }]:add(-dataMean[t])
102 | testData.data[{ {},t,{},{} }]:div(dataStd[t])
103 | print(t.."ch mean:"..dataMean[t].." stdv:"..dataStd[t])
104 | end
105 |
106 | testData = {
107 | data = testData.data,
108 | labels = testData.label,
109 | size = function() return numAllTestData end
110 | }
111 |
112 |
113 | -- test function
114 | function testRapid(dataset)
115 |
116 | model:evaluate()
117 |
118 | -- local vars
119 | local testError = 0
120 | local time = sys.clock()
121 |
122 | if average then
123 | cachedparams = parameters:clone()
124 | parameters:copy(average)
125 | end
126 |
127 |
128 | local numErr = 0
129 |
130 | for t = 1,dataset:size(),opt.testBatchSize do
131 |
132 | local batchStart = t
133 | local batchEnd = math.min(t+opt.testBatchSize-1,dataset:size())
134 | local inputs = dataset.data[{{batchStart,batchEnd},{},{},{}}]
135 | local targets = dataset.labels[{{batchStart,batchEnd}}]
136 |
137 | local preds = model:forward(inputs)
138 | for i = 1, math.min(opt.testBatchSize,dataset:size()-t+1) do
139 | confusion:add(preds[i], targets[i])
140 | table.insert(probList, preds[i]:clone())
141 | table.insert(labelList, targets[i])
142 | end
143 |
144 | err = criterion:forward(preds, targets)
145 | testError = testError + err
146 | numErr = numErr + 1
147 | end
148 |
149 | -- timing
150 | time = sys.clock() - time
151 | allTime = time
152 | time = time / dataset:size()
153 | print(" time to test 1 sample = " .. (time*1000) .. 'ms')
154 | print(" time to test all samples = " .. (allTime*1000) .. 'ms')
155 |
156 | -- testing error estimation
157 | testError = testError / numErr
158 |
159 | local testAccuracy = confusion.totalValid * 100
160 | confusion:zero()
161 |
162 | -- averaged param use?
163 | if average then
164 | -- restore parameters
165 | parameters:copy(cachedparams)
166 | end
167 |
168 | return testAccuracy, testError
169 | end
170 |
171 | testAcc, testErr = testRapid(testData)
172 |
173 |
174 | falsePositiveList = {}
175 | falseNegativeList = {}
176 | truePositiveList = {}
177 |
178 | splitTestDataPath = string.split(opt.testDataPath,'/')
179 | hdf5Name = string.split(splitTestDataPath[#splitTestDataPath],'.hdf5')
180 | csvDataPath = opt.threshold..'_'..hdf5Name[1]..'.csv'
181 | lfs.mkdir(hdf5Name[1])
182 |
183 | confusion = {{0,0},{0,0}}
184 |
185 | for i=1, #probList do
186 | if labelList[i] == 1 then
187 | if probList[i][1] >= math.log(opt.threshold) then
188 | table.insert(truePositiveList,{probList[i][1],testIdx[i][1],testIdx[i][2]})
189 | confusion[1][1] = confusion[1][1] + 1
190 | else
191 | table.insert(falseNegativeList,{probList[i][1],testIdx[i][1],testIdx[i][2]})
192 | confusion[1][2] = confusion[1][2] + 1
193 | end
194 | else
195 | if probList[i][1] >= math.log(opt.threshold) then
196 | table.insert(falsePositiveList,{probList[i][1],testIdx[i][1],testIdx[i][2]})
197 | confusion[2][1] = confusion[2][1] + 1
198 | else
199 | confusion[2][2] = confusion[2][2] + 1
200 | end
201 | end
202 | end
203 |
204 | str = {'ConfusionMatrix:\n'}
205 | table.insert(str, "[[ "..confusion[1][1].." "..confusion[1][2].."] \n")
206 | table.insert(str, " [ "..confusion[2][1].." "..confusion[2][2].."]] \n")
207 | print(table.concat(str))
208 |
209 |
210 | f = io.open(hdf5Name[1].."/FP_"..csvDataPath,"w")
211 | for i=1, #falsePositiveList do
212 | f:write(falsePositiveList[i][1]..","..falsePositiveList[i][2]..","..falsePositiveList[i][3].."\n")
213 | end
214 | f:close()
215 |
216 | f = io.open(hdf5Name[1].."/TP_"..csvDataPath,"w")
217 | for i=1, #truePositiveList do
218 | f:write(truePositiveList[i][1]..","..truePositiveList[i][2]..","..truePositiveList[i][3].."\n")
219 | end
220 | f:close()
221 |
222 | f = io.open(hdf5Name[1].."/FN_"..csvDataPath,"w")
223 | for i=1, #falseNegativeList do
224 | f:write(falseNegativeList[i][1]..","..falseNegativeList[i][2]..","..falseNegativeList[i][3].."\n")
225 | end
226 | f:close()
227 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MUSIC for P3 dataset
2 |
3 | ## Overview
4 |
5 | The number of photovoltaic power plants is growing so rapidly that we must rely on satellitel observations and efficient machine learning methods for the global monitoring. **MUSIC** for P3 (**P**hotovoltaic **P**ower **P**lants) is a training and validation dataset generated by [AIRC/AIST](http://www.airc.aist.go.jp/en/) to support such a global survey of photovoltaic power plants.
6 |
7 |
8 | We have picked up the photovoltaic power plants listed on [the website of Electrical Japan](http://agora.ex.nii.ac.jp/earthquake/201103-eastjapan/energy/electrical-japan/type/8.html.ja) only if they generated more than 5MW electricity and the construction had been completed by 2015. The multiband satellite images of these target areas, taken by [Landsat-8](https://landsat.usgs.gov/landsat-8), were cropped into a 16 × 16 grid covering a 480 × 480 meter area as shown below.
9 |
10 | 
11 |
12 |
13 | These patch images are classified as “positives” if the solar panels cover more than 20% of the total areas, while patches with no solar panels are classified as “negatives”. The rest with the intermediate coverage (0~20%) were neither “positives” and “negatives”.
14 |
15 |
16 | You can download the **MUSIC** for P3 dataset with two different format ([HDF5](https://github.com/gistairc/MUSIC4P3#hdf5) and [GeoTiff](https://github.com/gistairc/MUSIC4P3#tiff)) along with the source code for the detection and classification. More detailed exaplanations can be found in the following papers.
17 |
18 | [1] *Tomohiro Ishii, Edgar Simo-Serra, Satoshi Iizuka, Yoshihiko Mochizuki, Akihiro Sugimoto, Ryosuke Nakamura, Hiroshi Ishikawa ,"Detection by Classification of Buildings in Multispectral Satellite Imagery," ICPR 2016.* (http://www.f.waseda.jp/hfs/IshiiICPR2016.pdf)
19 |
20 | [2] *Nevrez Imamoglu, Motoki Kimura, Hiroki Miyamoto, Aito Fujita, Ryosuke Nakamura,"Solar Power Plant Detection on Multi-Spectral Satellite Imagery using Weakly-Supervised CNN with Feedback Features and m-PCNN Fusion," BMVC 2017.* (https://arxiv.org/abs/1704.06410)
21 |
22 | ##### Updated dataset (V2)
23 | It should be noted here that the "negatives" in the initial dataset (V1) is contaminated by small-scale photovoltaic power plants not included in the [original inventory](http://agora.ex.nii.ac.jp/earthquake/201103-eastjapan/energy/electrical-japan/type/8.html.ja). After publishing these papers, we have checked all the false positives through in-site survey or high-resolution imagery. In fact, some of "false positives" are found to be relatievely small photovoltaic power plants unlisted in the original database. Therefore, we've produced updated V2 dataset by correcting these misclassification. The performance of our previous works was estimated with V1 dataset, but more accurate estimate can be achieved by using this updated dataset V2.
24 |
25 |
26 |
27 | ## Download
28 | **IMPORTANT** -- Please read the [Terms of Use](https://github.com/gistairc/MUSIC4P3/blob/master/LICENSE.md) before downloading the MUSIC4P3 dataset.
29 |
30 |
31 | #### hdf5
32 | HDF5 version (for using [Torch](https://github.com/gistairc/MUSIC4P3#torch-ver) version code) of the dataset can be downloaded from [here](http://data.airc.aist.go.jp/MUSIC4P3dataset/MUSIC4P3data_hdf.zip) (4.6GB) .
33 | Or type the following in the terminal.
34 |
35 | ```
36 | $ wget http://data.airc.aist.go.jp/MUSIC4P3dataset/MUSIC4P3data_hdf.zip
37 | $ unzip MUSIC4P3data_hdf.zip
38 | ```
39 |
40 | The directory configuration in the unzipped folder:
41 | ```
42 | ./torch/resource/
43 | train/
44 | LC81060302015147LGN00.hdf5
45 | LC81070302015266LGN00.hdf5 ...
46 | val/
47 | LC81060302015147LGN00.hdf5
48 | LC81070302015266LGN00.hdf5 ...
49 | test/
50 | LC81060302015147LGN00.hdf5
51 | LC81070302015266LGN00.hdf5 ...
52 |
53 |
54 | ```
55 |
56 |
57 | [V2](https://github.com/gistairc/MUSIC4P3/blob/master/README.md#update-data-v2) dataset is [here](http://data.airc.aist.go.jp/MUSIC4P3dataset/MUSIC4P3data_hdf_v2.zip).
58 |
59 |
60 | #### tiff
61 | Tiff version (for using [Chainer](https://github.com/gistairc/MUSIC4P3#chainer-ver) version code) of the dataset can be downloaded from [here](http://data.airc.aist.go.jp/MUSIC4P3dataset/MUSIC4P3data_tiff.zip) (4.2GB) .
62 | Or type the following in the terminal.
63 | ```
64 | $ wget http://data.airc.aist.go.jp/MUSIC4P3dataset/MUSIC4P3data_tiff.zip
65 | $ unzip MUSIC4P3data_tiff.zip
66 | ```
67 | The directory configuration in the unzipped files is as follows:
68 | ```
69 | ./chainer/resource/
70 | train/
71 | positive/
72 | LC81060302015147LGN00_274_300_2064.tiff
73 | LC81060302015147LGN00_274_300_13289.tiff ...
74 | negative/
75 | LC81060302015147LGN00_100_105.tiff
76 | LC81060302015147LGN00_100_114.tiff ...
77 | val/
78 | positive/
79 | LC81070352015298LGN00_206_266.tiff
80 | LC81070352015298LGN00_374_160.tiff ...
81 | negative/
82 | LC81060302015147LGN00_2_86.tiff
83 | LC81060302015147LGN00_4_88.tiff ...
84 | test/
85 | positive/
86 | LC81060302015147LGN00_79_323.tiff
87 | LC81060302015147LGN00_80_323.tiff ...
88 | negative/
89 | LC81060302015147LGN00_100_100.tiff
90 | LC81060302015147LGN00_100_101.tiff ...
91 | ```
92 |
93 | [V2](https://github.com/gistairc/MUSIC4P3/blob/master/README.md#update-data-v2) dataset is [here](http://data.airc.aist.go.jp/MUSIC4P3dataset/MUSIC4P3data_tiff_v2.zip).
94 |
95 | ## Source
96 | ### Torch ver
97 | #### Requierment
98 | * torch7
99 | For torch installition see http://torch.ch/
100 | * torch_toolbox
101 | Download from https://github.com/e-lab/torch-toolbox
102 | Unzip and put the it on the same directory.
103 | ```
104 | FOR EXAMPLE
105 | torch/
106 | resource
107 | torch-toolbox
108 | train-cnn.lua
109 | train-cnn.sh
110 | test-cnn.lua
111 | test-cnn.sh ...
112 | ```
113 |
114 | #### Training
115 | For trainging, type the followingin the terminal.
116 |
117 | ```
118 | $ sh train-cnn.sh
119 | ```
120 | Models are saved to "./ishiinet_p1n15/cnn_nm_epXXXX.net" (the output file name is specified in train-cnn.sh) .
121 |
122 | #### Testing
123 |
124 | Type the following in the terminal.
125 |
126 | ```
127 | $ sh test-cnn.sh
128 | ```
129 | Result file are saved to "./ishiinet_p1n15/test_th0.999_all_cm.txt".
130 | Can be changed by passing in this shell file.
131 | If you want to the same result from paper\[1\], please use this [pre training model.](http://data.airc.aist.go.jp/MUSIC4P3dataset/cnn_nm_ep1930.net)
132 |
133 |
134 | ### Chainer ver
135 | #### Requierment
136 | * Python 2.7.*
137 | * Chainer 1.24.*
138 | For chainer Installition, see from https://chainer.org/
139 |
140 | #### Training
141 | Type the following in the terminal.
142 |
143 | ```
144 | $ sh train.sh
145 | ```
146 |
147 | Models are save to ./result/ (the output directory is specified in train.sh) .
148 |
149 | #### Testing
150 |
151 | Type the following in the terminal.
152 |
153 | ```
154 | $ sh test.sh > result.log
155 | ```
156 |
157 | Result file are created to ./result.log .
158 | If you want to the same result from paper\[1\], please use this [pre training model.](http://data.airc.aist.go.jp/MUSIC4P3dataset/newmegasolarCNN_NS_72720_3725_iteration.model)
159 |
160 | ## Acknowledgement
161 | This dataset and source code are based on results obtained from a project commissioned by the New Energy and Industrial Technology Development Organization (NEDO).
162 |
--------------------------------------------------------------------------------
/torch/train-cnn.lua:
--------------------------------------------------------------------------------
1 |
2 | require 'cunn'
3 | require 'optim'
4 | require 'image'
5 | require 'hdf5'
6 | require 'cutorch'
7 | require 'lfs'
8 |
9 | package.path = package.path..';torch-toolbox-master/Sanitize/?.lua'
10 |
11 | sanitize = require 'sanitize'
12 |
13 | fname = 'train-cnn'
14 |
15 |
16 | --
17 | cmd = torch.CmdLine()
18 | cmd:text()
19 | cmd:text('Megasolar Training')
20 | cmd:text()
21 | cmd:text('Options:')
22 | cmd:option('-save', fname, 'subdirectory to save/log experiments in')
23 | cmd:option('-network', '', 'reload pretrained network')
24 | cmd:option('-model', 'convnet', 'type of model to train: convnet | mlp | linear')
25 | cmd:option('-full', false, 'use full dataset (50,000 samples)')
26 | cmd:option('-visualize', false, 'visualize input data and weights during training')
27 | cmd:option('-seed', 1, 'fixed input seed for repeatable experiments')
28 | cmd:option('-optimization', 'SGD', 'optimization method: SGD | ASGD | CG | LBFGS')
29 | cmd:option('-learningRate', 1e-3, 'learning rate at t=0')
30 | cmd:option('-batchSize', 32, 'mini-batch size (1 = pure stochastic)')
31 | cmd:option('-weightDecay', 0, 'weight decay (SGD only)')
32 | cmd:option('-momentum', 0.9, 'momentum (SGD only)')
33 | cmd:option('-t0', 1, 'start averaging at t0 (ASGD only), in nb of epochs')
34 | cmd:option('-maxIter', 5, 'maximum nb of iterations for CG and LBFGS')
35 | cmd:option('-threads', 2, 'nb of threads to use')
36 | cmd:option('-testInterval', 10, 'test interval')
37 | cmd:option('-saveInterval', 500, 'save interval')
38 | cmd:option('-inputChannel', 7, 'nb of input channels')
39 | cmd:option('-maxEpoch', 1000, 'maximum nb of epochs')
40 | cmd:option('-testBatchSize', 10000, 'test batch size')
41 | cmd:option('-gpu', 1, 'id of gpu to use')
42 | cmd:option('-dropout', false, '')
43 | cmd:option('-batchNorm', false, '')
44 | cmd:option('-negativeBatchSize', 128, 'negative batch size for negative mining')
45 | cmd:option('-batchSizeNM', 32, 'mini-batch size for negative mining')
46 | cmd:option('-epochStartNM', 5000, 'nb of epochs to start negative mining')
47 | cmd:option('-trainDataPath', '', 'path of hdf5 dataset for training')
48 | cmd:option('-testDataPath', '', 'path of hdf5 dataset for test')
49 | cmd:option('-negativeRatio', 1, 'batch ratio, 1 : x = positive : negative')
50 | cmd:option('-trainnorm', 'mean_stdv.csv', 'output training data mean and stdv path')
51 | cmd:text()
52 | opt = cmd:parse(arg)
53 | --
54 |
55 | print (opt.optimization)
56 |
57 | cutorch.manualSeed(opt.seed)
58 | cutorch.setDevice(opt.gpu)
59 |
60 | classes = {'positive', 'negative'}
61 | accLogger = optim.Logger(paths.concat(opt.save, 'accuracy.log'))
62 | errLogger = optim.Logger(paths.concat(opt.save, 'error.log' ))
63 |
64 | if opt.network == '' then
65 | -- define model to train
66 | model = nn.Sequential()
67 | if opt.model == 'convnet' then
68 | ------------------------------------------------------------
69 | -- convolutional network
70 | ------------------------------------------------------------
71 | -- stage 1
72 | model:add(nn.SpatialConvolution(opt.inputChannel, 32, 3, 3))
73 | model:add(nn.ReLU())
74 | if opt.batchNorm ~= false then
75 | model:add(nn.SpatialBatchNormalization(32))
76 | end
77 |
78 | -- stage 2
79 | model:add(nn.SpatialConvolution(32, 32, 3, 3))
80 | model:add(nn.ReLU())
81 | if opt.batchNorm ~= false then
82 | model:add(nn.SpatialBatchNormalization(32))
83 | end
84 |
85 | -- stage 3
86 | model:add(nn.SpatialConvolution(32, 32, 3, 3))
87 | model:add(nn.ReLU())
88 | if opt.batchNorm ~= false then
89 | model:add(nn.SpatialBatchNormalization(32))
90 | end
91 |
92 |
93 | -- stage 4
94 | model:add(nn.Reshape(32*10*10))
95 | if opt.dropout ~= false then
96 | model:add(nn.Dropout(0.5))
97 | end
98 | model:add(nn.Linear(32*10*10,#classes))
99 |
100 | ------------------------------------------------------------
101 | else
102 | print('Unknown model type')
103 | cmd:text()
104 | error()
105 | end
106 | else
107 | print(' reloading previously trained network')
108 | model = nn.Sequential()
109 | model = torch.load(opt.network)
110 | end
111 |
112 | -- verbose
113 | print('using model:')
114 | print(model)
115 |
116 | ----------------------------------------------------------------------
117 | -- loss function: negative log-likelihood
118 | --
119 | model:add(nn.LogSoftMax())
120 | model:cuda()
121 |
122 | -- retrieve parameters and gradients
123 | parameters,gradParameters = model:getParameters()
124 |
125 | criterion = nn.ClassNLLCriterion():cuda()
126 |
127 | ----------------------------------------------------------------------
128 | -- Import Data
129 | --
130 |
131 | function getFileList(dirPath, ext)
132 | list = {}
133 | for file in lfs.dir(dirPath) do
134 | if string.find(file, ext..'$') then
135 | table.insert(list,file)
136 | end
137 | end
138 | return list
139 | end
140 |
141 | numTrainPositive = 0 -- the number of training positive data
142 | numTrainNegative = 0 -- the number of training negative data
143 |
144 | if lfs.attributes(opt.trainDataPath)['mode'] == 'file' then
145 | -- import training data
146 | print("Import training data...")
147 | myFile = hdf5.open(opt.trainDataPath,'r')
148 | trainData = myFile:read(''):all()
149 | myFile:close()
150 | labelNum = trainData.label:size()[1]
151 |
152 | for i = 1,labelNum do
153 | if trainData.label[i] == 0 then
154 | trainData.label[i] = 1
155 | numTrainPositive = numTrainPositive + 1
156 | else
157 | trainData.label[i] = 2
158 | numTrainNegative = numTrainNegative + 1
159 | end
160 | end
161 |
162 | numAllTrainData = labelNum
163 |
164 | elseif lfs.attributes(opt.trainDataPath)['mode'] == 'directory' then
165 | hdfList = getFileList(opt.trainDataPath,'hdf5')
166 | dataList = {}
167 | for i=1, #hdfList do
168 | local myFile = hdf5.open(opt.trainDataPath..'/'..hdfList[i])
169 | print('Importing:'..opt.trainDataPath..'/'..hdfList[i])
170 | tempTrainData = myFile:read(''):all()
171 | myFile:close()
172 | labelNum = tempTrainData.label:size()[1]
173 |
174 | for i = 1,labelNum do
175 | if tempTrainData.label[i] == 0 then
176 | tempTrainData.label[i] = 1
177 | numTrainPositive = numTrainPositive + 1
178 | else
179 | tempTrainData.label[i] = 2
180 | numTrainNegative = numTrainNegative + 1
181 | end
182 | end
183 | table.insert(dataList, tempTrainData)
184 | end
185 | numAllTrainData = 0
186 | for i=1, #dataList do
187 | numAllTrainData = numAllTrainData + dataList[i]['data']:size()[1]
188 | end
189 | print(numAllTrainData)
190 | trainData = {
191 | data=torch.Tensor(numAllTrainData, opt.inputChannel, dataList[1]['data']:size()[3], dataList[1]['data']:size()[4]),
192 | label=torch.Tensor(numAllTrainData),
193 | size = function() return numAllTrainData end
194 | }
195 | k = 1
196 | for i=1, #dataList do
197 | for j=1, dataList[i]['data']:size()[1] do
198 | trainData.data[k] = dataList[i]['data'][j]
199 | trainData.label[k] = dataList[i]['label'][j]
200 | k = k + 1
201 | end
202 | end
203 |
204 | else
205 | error("invalid dataset")
206 | end
207 |
208 | dataMean = {}
209 | dataStd = {}
210 |
211 | file = io.open(opt.trainnorm,"w")
212 | for t=1,opt.inputChannel do
213 | local trainMean = trainData.data[{{},t,{},{}}]:mean()
214 | local trainStd = trainData.data[{{},t,{},{}}]:std()
215 | trainData.data[{ {},t,{},{} }]:add(-trainMean)
216 | trainData.data[{ {},t,{},{} }]:div(trainStd)
217 | print(t.."ch mean:"..trainMean.." stdv:"..trainStd)
218 | file:write(trainMean..","..trainStd.."\n")
219 | table.insert(dataMean, trainMean)
220 | table.insert(dataStd, trainStd)
221 | end
222 | file:close()
223 |
224 | positiveList = {}
225 | negativeList = {}
226 | for i = 1, trainData:size() do
227 | if trainData.label[i] == 1 then
228 | table.insert(positiveList,i)
229 | else
230 | table.insert(negativeList,i)
231 | end
232 | end
233 |
234 | trainData = {
235 | data = trainData.data,
236 | labels = trainData.label,
237 | size = function() return numAllTrainData end,
238 | addressList = {positiveList,negativeList}
239 | }
240 |
241 |
242 |
243 | -- import test data
244 | if lfs.attributes(opt.testDataPath)['mode'] == 'file' then
245 |
246 | print("Import test data...")
247 | print('Importing:'..opt.testDataPath)
248 | myFile = hdf5.open(opt.testDataPath,'r')
249 | testData = myFile:read(''):all()
250 | myFile:close()
251 | labelNum = testData.label:size()[1]
252 |
253 | for i = 1,labelNum do
254 | if testData.label[i] == 0 then
255 | testData.label[i] = 1
256 | else
257 | testData.label[i] = 2
258 | end
259 | end
260 |
261 | numAllTestData = labelNum
262 |
263 | elseif lfs.attributes(opt.testDataPath)['mode'] == 'directory' then
264 | hdfList = getFileList(opt.testDataPath,'hdf5')
265 | dataList = {}
266 | for i=1, #hdfList do
267 | local myFile = hdf5.open(opt.testDataPath..'/'..hdfList[i])
268 | print('Importing:'..opt.testDataPath..'/'..hdfList[i])
269 | tempTestData = myFile:read(''):all()
270 | myFile:close()
271 | labelNum = tempTestData.label:size()[1]
272 |
273 | for i = 1,labelNum do
274 | if tempTestData.label[i] == 0 then
275 | tempTestData.label[i] = 1
276 | else
277 | tempTestData.label[i] = 2
278 | end
279 | end
280 | table.insert(dataList, tempTestData)
281 | end
282 | numAllTestData = 0
283 | for i=1, #dataList do
284 | numAllTestData = numAllTestData + dataList[i]['data']:size()[1]
285 | end
286 | print(numAllTestData)
287 | testData = {
288 | data=torch.Tensor(numAllTestData, opt.inputChannel, dataList[1]['data']:size()[3], dataList[1]['data']:size()[4]),
289 | label=torch.Tensor(numAllTestData),
290 | size = function() return numAllTestData end
291 | }
292 | k = 1
293 | for i=1, #dataList do
294 | for j=1, dataList[i]['data']:size()[1] do
295 | testData.data[k] = dataList[i]['data'][j]
296 | testData.label[k] = dataList[i]['label'][j]
297 | k = k + 1
298 | end
299 | end
300 |
301 | else
302 | error("invalid dataset")
303 | end
304 |
305 | for t=1,opt.inputChannel do
306 | testData.data[{ {},t,{},{} }]:add(-dataMean[t])
307 | testData.data[{ {},t,{},{} }]:div(dataStd[t])
308 | print(t.."ch mean:"..dataMean[t].." stdv:"..dataStd[t])
309 | end
310 |
311 | testData = {
312 | data = testData.data,
313 | labels = testData.label,
314 | size = function() return numAllTestData end
315 | }
316 |
317 | ----------------------------------------------------------------------
318 | -- define training and testing functions
319 | --
320 |
321 | -- this matrix records the current confusion across classes
322 | confusion = optim.ConfusionMatrix(classes)
323 |
324 | -- log results to files
325 | print (opt.save)
326 | accLogger.showPlot = false
327 | errLogger.showPlot = false
328 |
329 | function dataAugmentation(batchData)
330 | outputBatchData = torch.Tensor(batchData:size()[1],batchData:size()[2],batchData:size()[3],batchData:size()[4]):float()
331 | inputBatchSize = batchData:size()[1]
332 | hRand = torch.rand(inputBatchSize)
333 | vRand = torch.rand(inputBatchSize)
334 | for i=1, inputBatchSize do
335 | if hRand[i] > 0.5 then
336 | outputBatchData[i] = image.hflip(batchData[i]:float())
337 | else
338 | outputBatchData[i] = batchData[i]:float()
339 | end
340 | if vRand[i] > 0.5 then
341 | outputBatchData[i] = image.vflip(outputBatchData[i])
342 | end
343 | end
344 | return outputBatchData
345 | end
346 |
347 | -- training function
348 | function train(dataset)
349 | -- epoch tracker
350 | epoch = epoch or 1
351 |
352 | model:training()
353 |
354 | -- local vars
355 | local time = sys.clock()
356 | local trainError = 0
357 |
358 | -- do one epoch
359 | print(' on training set:')
360 | print(" online epoch # " .. epoch .. ' [batchSize = ' .. opt.batchSize .. ']')
361 | for t = 1,dataset:size(),opt.batchSize do
362 |
363 |
364 | local inputs = dataset.data[{{t,math.min(t+opt.batchSize-1,dataset:size())},{},{},{}}]
365 | local targets = dataset.labels[{{t,math.min(t+opt.batchSize-1,dataset:size())}}]
366 |
367 | -- data augmentation
368 | inputs = dataAugmentation(inputs):cuda()
369 | targets = targets:cuda()
370 |
371 | -- create closure to evaluate f(X) and df/dX
372 | local feval = function(x)
373 | -- get new parameters
374 | if x ~= parameters then
375 | parameters:copy(x)
376 | end
377 |
378 | -- reset gradients
379 | gradParameters:zero()
380 |
381 | -- f is the average of all criterions
382 | local f = 0
383 |
384 |
385 | local output = model:forward(inputs)
386 | local err = criterion:forward(output, targets)
387 | f = f + err
388 | -- estimate df/dW
389 | local df_do = criterion:backward(output, targets)
390 | model:backward(inputs, df_do)
391 | for i = 1,math.min(opt.batchSize,dataset:size()-t+1) do
392 | -- update confusion
393 | confusion:add(output[i], targets[i])
394 | end
395 |
396 |
397 | trainError = trainError + f
398 |
399 | -- return f and df/dX
400 | return f,gradParameters
401 | end
402 |
403 | -- optimize on current mini-batch
404 | if opt.optimization == 'CG' then
405 | config = config or {maxIter = opt.maxIter}
406 | optim.cg(feval, parameters, config)
407 |
408 | elseif opt.optimization == 'LBFGS' then
409 | config = config or {learningRate = opt.learningRate,
410 | maxIter = opt.maxIter,
411 | nCorrection = 10}
412 | optim.lbfgs(feval, parameters, config)
413 |
414 | elseif opt.optimization == 'SGD' then
415 | config = config or {learningRate = opt.learningRate,
416 | weightDecay = opt.weightDecay,
417 | momentum = opt.momentum,
418 | learningRateDecay = 5e-7}
419 | optim.sgd(feval, parameters, config)
420 |
421 | elseif opt.optimization == 'ASGD' then
422 | config = config or {eta0 = opt.learningRate,
423 | t0 = nbTrainingPatches * opt.t0}
424 | _,_,average = optim.asgd(feval, parameters, config)
425 |
426 | elseif opt.optimization == 'ADAM' then
427 | config = config or {learningRate = opt.learningRate,
428 | learningRateDecay = 5e-7,
429 | beta1 = 0.9,
430 | beta2 = 0.999,
431 | epsilon = 1e-8,
432 | weightDecay = opt.weightDecay}
433 | optim.adam(feval, parameters, config)
434 |
435 | else
436 | error('unknown optimization method')
437 | end
438 | end
439 |
440 | -- train error
441 | trainError = trainError / math.floor(dataset:size()/opt.batchSize)
442 |
443 | -- time taken
444 | time = sys.clock() - time
445 | allTime = time
446 | time = time / dataset:size()
447 | print(" time to learn 1 sample = " .. (time*1000) .. 'ms')
448 | print(" time to learn all samples = " .. (allTime*1000) .. 'ms')
449 |
450 | -- print confusion matrix
451 | print(confusion)
452 | local trainAccuracy = confusion.totalValid * 100
453 | confusion:zero()
454 |
455 | -- save/log current net
456 | if epoch%opt.saveInterval==0 then
457 | local filename = paths.concat(opt.save, 'cnn_ep'..epoch..'.net')
458 | os.execute('mkdir -p ' .. paths.dirname(filename))
459 | if paths.filep(filename) then
460 | os.execute('mv ' .. filename .. ' ' .. filename .. '.old')
461 | end
462 | print(' saving network to '..filename)
463 | torch.save(filename, sanitize(model))
464 | end
465 |
466 | -- next epoch
467 | epoch = epoch + 1
468 |
469 | return trainAccuracy, trainError
470 | end
471 |
472 | -- training function using negative mining
473 | function trainMod(dataset)
474 | -- epoch tracker
475 | epoch = epoch or 1
476 |
477 | model:training()
478 |
479 |
480 |
481 | -- local vars
482 | local time = sys.clock()
483 | local trainError = 0
484 |
485 | batch_rate = 1 + opt.negativeRatio
486 |
487 | -- do one epoch
488 | print(' on training set:')
489 | print(" online epoch # " .. epoch .. ' [batchSize = ' .. opt.batchSize .. ']')
490 |
491 |
492 | local dataSize = dataset.data:size()
493 | pIdx = torch.randperm(#dataset.addressList[1])
494 | nIdx = torch.randperm(#dataset.addressList[2])
495 | addressListmin=math.min(#dataset.addressList[1],#dataset.addressList[2])
496 | BatchData = {
497 | data = torch.Tensor(addressListmin*batch_rate,dataSize[2],dataSize[3],dataSize[4]),
498 | labels = torch.Tensor(addressListmin*batch_rate),
499 | size = function() return addressListmin*batch_rate end,
500 | }
501 |
502 | for i=1, addressListmin do
503 | BatchData.data[batch_rate*i-batch_rate+1] = dataset.data[dataset.addressList[1][pIdx[i]]]
504 | BatchData.labels[batch_rate*i-batch_rate+1] = dataset.labels[dataset.addressList[1][pIdx[i]]]
505 | for j=1, batch_rate-1 do
506 | BatchData.data[batch_rate*i-batch_rate+j+1] = dataset.data[dataset.addressList[2][nIdx[(batch_rate-1)*i+1-j]]]
507 | BatchData.labels[batch_rate*i-batch_rate+j+1] = dataset.labels[dataset.addressList[2][nIdx[(batch_rate-1)*i+1-j]]]
508 | end
509 | end
510 |
511 |
512 | for t = 1,BatchData:size(),opt.batchSize do
513 |
514 |
515 | local inputs = BatchData.data[{{t,math.min(t+opt.batchSize-1,BatchData:size())},{},{},{}}]
516 | local targets = BatchData.labels[{{t,math.min(t+opt.batchSize-1,BatchData:size())}}]
517 |
518 | -- data augmentation
519 | inputs = dataAugmentation(inputs):cuda()
520 | targets = targets:cuda()
521 |
522 | -- create closure to evaluate f(X) and df/dX
523 | local feval = function(x)
524 | -- get new parameters
525 | if x ~= parameters then
526 | parameters:copy(x)
527 | end
528 |
529 | -- reset gradients
530 | gradParameters:zero()
531 |
532 | -- f is the average of all criterions
533 | local f = 0
534 |
535 |
536 | local output = model:forward(inputs)
537 | local err = criterion:forward(output, targets)
538 | f = f + err
539 | -- estimate df/dW
540 | local df_do = criterion:backward(output, targets)
541 | model:backward(inputs, df_do)
542 | for i = 1,math.min(opt.batchSize,BatchData:size()-t+1) do
543 | -- update confusion
544 | confusion:add(output[i], targets[i])
545 | end
546 |
547 | -- normalize gradients and f(X)
548 |
549 | trainError = trainError + f
550 |
551 | -- return f and df/dX
552 | return f,gradParameters
553 | end
554 |
555 | -- optimize on current mini-batch
556 | if opt.optimization == 'CG' then
557 | config = config or {maxIter = opt.maxIter}
558 | optim.cg(feval, parameters, config)
559 |
560 | elseif opt.optimization == 'LBFGS' then
561 | config = config or {learningRate = opt.learningRate,
562 | maxIter = opt.maxIter,
563 | nCorrection = 10}
564 | optim.lbfgs(feval, parameters, config)
565 |
566 | elseif opt.optimization == 'SGD' then
567 | config = config or {learningRate = opt.learningRate,
568 | weightDecay = opt.weightDecay,
569 | momentum = opt.momentum,
570 | learningRateDecay = 5e-7}
571 | optim.sgd(feval, parameters, config)
572 |
573 | elseif opt.optimization == 'ASGD' then
574 | config = config or {eta0 = opt.learningRate,
575 | t0 = nbTrainingPatches * opt.t0}
576 | _,_,average = optim.asgd(feval, parameters, config)
577 |
578 | elseif opt.optimization == 'ADAM' then
579 | config = config or {learningRate = opt.learningRate,
580 | learningRateDecay = 5e-7,
581 | beta1 = 0.9,
582 | beta2 = 0.999,
583 | epsilon = 1e-8,
584 | weightDecay = opt.weightDecay}
585 | optim.adam(feval, parameters, config)
586 | else
587 | error('unknown optimization method')
588 | end
589 | end
590 |
591 | -- train error
592 | trainError = trainError / math.floor(BatchData:size()/opt.batchSize)
593 |
594 | -- time taken
595 | time = sys.clock() - time
596 | allTime = time
597 | time = time / BatchData:size()
598 | print(" time to learn 1 sample = " .. (time*1000) .. 'ms')
599 | print(" time to learn all samples = " .. (allTime*1000) .. 'ms')
600 |
601 | -- print confusion matrix
602 | print(confusion)
603 | local trainAccuracy = confusion.totalValid * 100
604 | confusion:zero()
605 |
606 | -- save/log current net
607 | if epoch%opt.saveInterval==0 then
608 | local filename = paths.concat(opt.save, 'cnn_nm_ep'..epoch..'.net')
609 | os.execute('mkdir -p ' .. paths.dirname(filename))
610 | if paths.filep(filename) then
611 | os.execute('mv ' .. filename .. ' ' .. filename .. '.old')
612 | end
613 | print(' saving network to '..filename)
614 | torch.save(filename, sanitize(model))
615 | end
616 |
617 | -- next epoch
618 | epoch = epoch + 1
619 |
620 | return trainAccuracy, trainError
621 | end
622 |
623 | -- training function using negative mining
624 | function trainNM(dataset)
625 | -- epoch tracker
626 | epoch = epoch or 1
627 |
628 | model:training()
629 |
630 | -- batchNM = 128
631 |
632 | -- local vars
633 | local time = sys.clock()
634 | local trainError = 0
635 |
636 | -- do one epoch
637 | print(' on training set:')
638 | print(" online epoch # " .. epoch .. ' [batchSize = ' .. opt.batchSizeNM .. ']')
639 |
640 |
641 | -- get 128 negative data
642 | i = 1
643 | local dataSize = dataset.data:size()
644 | local negativeList = {}
645 | local nInputs = torch.Tensor(opt.negativeBatchSize,dataSize[2],dataSize[3],dataSize[4])
646 |
647 |
648 | local nTargets = torch.Tensor(opt.negativeBatchSize)
649 |
650 | while i <= opt.negativeBatchSize do
651 | randN = (torch.rand(1)*(dataSize[1]-1)):floor()[1]+1
652 | j = 1
653 | count = 0
654 | while j <= #negativeList do
655 | if randN == negativeList[j] then
656 | count = count + 1
657 | end
658 | j = j + 1
659 | end
660 |
661 | if dataset.labels[randN] == 2 and count == 0 then
662 | table.insert(negativeList,randN)
663 |
664 | nInputs[i] = dataset.data[randN]:double()
665 |
666 | nTargets[i] = dataset.labels[randN]
667 |
668 | i = i + 1
669 | end
670 | end
671 |
672 |
673 |
674 | -- get all positive data
675 | positiveList = {}
676 | for i = 1, dataset:size() do
677 | if dataset.labels[i] == 1 then
678 | table.insert(positiveList,i)
679 | end
680 | end
681 |
682 | pIdx = torch.randperm(#positiveList)
683 |
684 |
685 | -- get loss of the negative data
686 | local output = model:forward(nInputs:cuda())
687 | --local err = criterion:forward(output, nTargets:cuda())
688 | local errList = torch.Tensor(opt.negativeBatchSize)
689 |
690 | for i = 1, opt.negativeBatchSize do
691 | errList[i] = criterion:forward(output[i], nTargets[i])
692 | end
693 |
694 |
695 | -- sort the loss in descending order
696 |
697 | y, idx = torch.sort(errList:double(),1,true)
698 |
699 | -- select 16 larger loss for training and create mini-batch
700 | local inputs = torch.Tensor(opt.batchSizeNM,dataSize[2],dataSize[3],dataSize[4]):cuda()
701 | local targets = torch.Tensor(opt.batchSizeNM):cuda()
702 | for i = 1, (opt.batchSizeNM/2) do
703 |
704 | inputs[i] = nInputs[idx[i]]
705 | targets[i] = nTargets[idx[i]]
706 | end
707 | for i=1, (opt.batchSizeNM/2) do
708 | inputs[i+(opt.batchSizeNM/2)] = dataset.data[positiveList[pIdx[i]]]
709 | targets[i+(opt.batchSizeNM/2)] = dataset.labels[positiveList[pIdx[i]]]
710 | end
711 |
712 |
713 | -- data augmentation
714 | inputs = dataAugmentation(inputs):cuda()
715 | targets:cuda()
716 |
717 | -- training
718 | -- create closure to evaluate f(X) and df/dX
719 | local feval = function(x)
720 | -- get new parameters
721 | if x ~= parameters then
722 | parameters:copy(x)
723 | end
724 |
725 | -- reset gradients
726 | gradParameters:zero()
727 |
728 | -- f is the average of all criterions
729 | local f = 0
730 |
731 | local output = model:forward(inputs)
732 | local err = criterion:forward(output, targets)
733 | f = f + err
734 | -- estimate df/dW
735 | local df_do = criterion:backward(output, targets)
736 | model:backward(inputs, df_do)
737 | for i = 1,opt.batchSizeNM do
738 | -- update confusion
739 | confusion:add(output[i], targets[i])
740 | end
741 |
742 | trainError = trainError + f
743 |
744 | -- return f and df/dX
745 | return f,gradParameters
746 | end
747 |
748 | -- optimize on current mini-batch
749 | if opt.optimization == 'CG' then
750 | config = config or {maxIter = opt.maxIter}
751 | optim.cg(feval, parameters, config)
752 |
753 | elseif opt.optimization == 'LBFGS' then
754 | config = config or {learningRate = opt.learningRate,
755 | maxIter = opt.maxIter,
756 | nCorrection = 10}
757 | optim.lbfgs(feval, parameters, config)
758 |
759 | elseif opt.optimization == 'SGD' then
760 | config = config or {learningRate = opt.learningRate,
761 | weightDecay = opt.weightDecay,
762 | momentum = opt.momentum,
763 | learningRateDecay = 5e-7}
764 | optim.sgd(feval, parameters, config)
765 |
766 | elseif opt.optimization == 'ASGD' then
767 | config = config or {eta0 = opt.learningRate,
768 | t0 = nbTrainingPatches * opt.t0}
769 | _,_,average = optim.asgd(feval, parameters, config)
770 |
771 | elseif opt.optimization == 'ADAM' then
772 | config = config or {learningRate = opt.learningRate,
773 | learningRateDecay = 5e-7,
774 | beta1 = 0.9,
775 | beta2 = 0.999,
776 | epsilon = 1e-8,
777 | weightDecay = opt.weightDecay}
778 | optim.adam(feval, parameters, config)
779 |
780 | else
781 | error('unknown optimization method')
782 | end
783 |
784 | -- train error
785 | -- time taken
786 | time = sys.clock() - time
787 | allTime = time
788 | print(" time to learn all samples = " .. (allTime*1000) .. 'ms')
789 |
790 | -- print confusion matrix
791 | print(confusion)
792 | local trainAccuracy = confusion.totalValid * 100
793 | confusion:zero()
794 |
795 | -- save/log current net
796 | if epoch%opt.saveInterval==0 then
797 | local filename = paths.concat(opt.save, 'cnn_nm_ep'..epoch..'.net')
798 | os.execute('mkdir -p ' .. paths.dirname(filename))
799 | if paths.filep(filename) then
800 | os.execute('mv ' .. filename .. ' ' .. filename .. '.old')
801 | end
802 | print(' saving network to '..filename)
803 | torch.save(filename, sanitize(model))
804 | end
805 |
806 | -- next epoch
807 | epoch = epoch + 1
808 |
809 | return trainAccuracy, trainError
810 | end
811 |
812 |
813 | -- test function
814 | function testRapid(dataset)
815 |
816 | model:evaluate()
817 |
818 | -- local vars
819 | local testError = 0
820 | local time = sys.clock()
821 |
822 | -- averaged param use?
823 | if average then
824 | cachedparams = parameters:clone()
825 | parameters:copy(average)
826 | end
827 |
828 | --local testBatch = 30000
829 | local numErr = 0
830 | --print('1')
831 | for t = 1,dataset:size(),opt.testBatchSize do
832 | -- disp progress
833 | -- xlua.progress(t, dataset:size())
834 | local batchStart = t
835 | local batchEnd = math.min(t+opt.testBatchSize-1,dataset:size())
836 | local inputs = dataset.data[{{batchStart,batchEnd},{},{},{}}]:cuda()
837 | local targets = dataset.labels[{{batchStart,batchEnd}}]:cuda()
838 |
839 | local preds = model:forward(inputs)
840 | for i = 1, math.min(opt.testBatchSize,dataset:size()-t+1) do
841 | confusion:add(preds[i], targets[i])
842 | end
843 |
844 | err = criterion:forward(preds, targets)
845 | testError = testError + err
846 | numErr = numErr + 1
847 | end
848 |
849 | -- timing
850 | time = sys.clock() - time
851 | allTime = time
852 | time = time / dataset:size()
853 | print(" time to test 1 sample = " .. (time*1000) .. 'ms')
854 | print(" time to test all samples = " .. (allTime*1000) .. 'ms')
855 |
856 | -- testing error estimation
857 | -- testError = testError / dataset:size()
858 | testError = testError / numErr
859 |
860 | -- print confusion matrix
861 | print(confusion)
862 | local testAccuracy = confusion.totalValid * 100
863 | confusion:zero()
864 |
865 | -- averaged param use?
866 | if average then
867 | -- restore parameters
868 | parameters:copy(cachedparams)
869 | end
870 |
871 | return testAccuracy, testError
872 | end
873 |
874 | n=1
875 |
876 | while n <= opt.maxEpoch do
877 | -- train/test
878 | if n <= opt.epochStartNM then
879 | trainAcc, trainErr = trainMod(trainData)
880 | if n%opt.testInterval == 0 or n == 1 then
881 | testAcc, testErr = testRapid(testData)
882 | end
883 | else
884 | -- negative mining
885 | trainAcc, trainErr = trainNM(trainData)
886 | if n%(opt.testInterval*10) == 0 then
887 | testAcc, testErr = testRapid(testData)
888 | end
889 | end
890 |
891 |
892 | -- update logger
893 | accLogger:add{['% train accuracy'] = trainAcc, ['% test accuracy'] = testAcc}
894 | errLogger:add{['% train error'] = trainErr, ['% test error'] = testErr}
895 |
896 | -- plot logger
897 | accLogger:style{['% train accuracy'] = '-', ['% test accuracy'] = '-'}
898 | errLogger:style{['% train error'] = '-', ['% test error'] = '-'}
899 | accLogger:plot()
900 | errLogger:plot()
901 |
902 | collectgarbage()
903 |
904 | collectgarbage()
905 |
906 | n = n + 1
907 | end
908 |
--------------------------------------------------------------------------------