-
Notifications
You must be signed in to change notification settings - Fork 0
/
transfer_learning_project.lua
161 lines (120 loc) · 4.76 KB
/
transfer_learning_project.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
require 'nn'
require 'image'
require 'optim'
logger = optim.Logger('Transfer.log') -- logger can be changed
logger:setNames{'Trainset Error', 'Testset Error'}
NUM_CLASSES = 4
dataset = torch.load('flowers.t7')
dataset = dataset:narrow(1,1,NUM_CLASSES)
classes = torch.range(1,NUM_CLASSES):totable() --17 classes
labels = torch.range(1,NUM_CLASSES):view(NUM_CLASSES,1):expand(NUM_CLASSES,80)
print(dataset:size()) --each class has 80 images of 3x128x128
image.display(dataset:select(2,20))
function shuffle(data,ydata) --shuffle data function
local RandOrder = torch.randperm(data:size(1)):long()
return data:index(1,RandOrder), ydata:index(1,RandOrder)
end
shuffledData, shuffledLabels = shuffle(dataset:view(-1,3,128,128), labels:contiguous():view(-1))
trainSize = 0.85 * shuffledData:size(1)
trainData, testData = unpack(shuffledData:split(trainSize, 1))
trainLabels, testLabels = unpack(shuffledLabels:split(trainSize, 1))
print(trainData:size())
trainData = trainData:float() -- convert the data from a ByteTensor to a float Tensor.
trainLabels = trainLabels:float()
mean, std = trainData:mean(), trainData:std()
print(mean, std)
trainData:add(-mean):div(std)
testData = testData:float()
testLabels = testLabels:float()
testData:add(-mean):div(std)
-- Load GoogLeNet
googLeNet = torch.load('GoogLeNet_v2_nn.t7')
-- The new network
model = nn.Sequential()
for i=1,10 do
local layer = googLeNet:get(i):clone()
layer.parameters = function() return {} end --disable parameters
layer.accGradParamters = nil --remove accGradParamters
model:add(layer)
end
-- Check output dimensions with random input
model:float()
local y = model:forward(torch.rand(1,3,128,128):float())
print(y:size())
-- Add the new layers
viewSize = 16 * 3 * 3
model:add(nn.SpatialConvolution(320, 16, 3, 3)) -- Input: 320, Output: 16, Kernel: 3X3, Stride: 1, Padding: 0
model:add(nn.ReLU())
model:add(nn.SpatialMaxPooling(4, 4, 4, 4)) -- Kernel: 4X4, Stride: 4
model:add(nn.View(viewSize)) -- View: Features * height * width (features: 16 - from SpatialConvolution, Width & Height: 128 / 4 - SpatialMaxPooling stride is 4)
model:add(nn.Dropout(0.5)) -- Dropout layer with 50% probability
model:add(nn.Linear(viewSize, NUM_CLASSES))
model:add(nn.LogSoftMax())
model:float()
-- Loss Function = Negative Log Likelihood ()
lossFunc = nn.ClassNLLCriterion():float()
w, dE_dw = model:getParameters()
print('Number of parameters:', w:nElement())
batchSize = 32
epochs = 200
optimState = {
learningRate = 0.1,
}
function forwardNet(data, labels, train)
--another helpful function of optim is ConfusionMatrix
local confusion = optim.ConfusionMatrix(torch.range(0,NUM_CLASSES - 1):totable())
local lossAcc = 0
local numBatches = 0
if train then
--set network into training mode
model:training()
end
for i = 1, data:size(1)-batchSize, batchSize do
numBatches = numBatches + 1
local x = data:narrow(1, i, batchSize):float()
local yt = labels:narrow(1, i, batchSize):float()
local y = model:forward(x)
local err = lossFunc:forward(y, yt)
lossAcc = lossAcc + err
confusion:batchAdd(y,yt)
if train then
function feval()
--model:zeroGradParameters() --zero grads
local dE_dy = lossFunc:backward(y,yt)
model:backward(x, dE_dy) -- backpropagation
return err, dE_dw
end
optim.adam(feval, w, optimState)
end
end
confusion:updateValids()
local avgLoss = lossAcc / numBatches
local avgError = 1 - confusion.totalValid
--print("train=========")
--print(train)
--print("train=========")
--print("confusion==============================")
--print(confusion)
--print("confusion==============================")
return avgLoss, avgError, tostring(confusion)
end
trainLoss = torch.Tensor(epochs)
testLoss = torch.Tensor(epochs)
trainError = torch.Tensor(epochs)
testError = torch.Tensor(epochs)
--reset net weights
model:apply(function(l) l:reset() end)
for e = 1, epochs do
trainData, trainLabels = shuffle(trainData, trainLabels) --shuffle training data
trainLoss[e], trainError[e] = forwardNet(trainData, trainLabels, true)
testLoss[e], testError[e], confusion = forwardNet(testData, testLabels, false)
logger:add{trainError[e],testError[e]} -- loss is the value which you want to plot
logger:style{'-','-'} -- the style of your line, as in MATLAB, we use '-' or '|' etc.
if e % 5 == 0 then
print('Epoch ' .. e .. ':')
print('Training error: ' .. trainError[e], 'Training Loss: ' .. trainLoss[e])
print('Test error: ' .. testError[e], 'Test Loss: ' .. testLoss[e])
print(confusion)
end
end
logger:plot()