Skip to content

Commit 87a8096

Browse files
authored
Merge pull request #1305 from NLGithubWP/dev-postgresql
Add the example data folder for SINGA peft
2 parents fbfb4a3 + 87dbf1a commit 87a8096

1 file changed

Lines changed: 399 additions & 0 deletions

File tree

Lines changed: 399 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,399 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one
3+
# or more contributor license agreements. See the NOTICE file
4+
# distributed with this work for additional information
5+
# regarding copyright ownership. The ASF licenses this file
6+
# to you under the Apache License, Version 2.0 (the
7+
# "License"); you may not use this file except in compliance
8+
# with the License. You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing,
13+
# software distributed under the License is distributed on an
14+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
# KIND, either express or implied. See the License for the
16+
# specific language governing permissions and limitations
17+
# under the License.
18+
#
19+
import argparse
20+
21+
from singa import device
22+
from singa import tensor
23+
from singa import opt
24+
import numpy as np
25+
import time
26+
from PIL import Image
27+
28+
from singa_peft import LinearLoraConfig, get_peft_model
29+
30+
np_dtype = {"float16": np.float16, "float32": np.float32}
31+
32+
singa_dtype = {"float16": tensor.float16, "float32": tensor.float32}
33+
34+
35+
# Data augmentation
36+
def augmentation(x, batch_size):
37+
xpad = np.pad(x, [[0, 0], [0, 0], [4, 4], [4, 4]], 'symmetric')
38+
for data_num in range(0, batch_size):
39+
offset = np.random.randint(8, size=2)
40+
x[data_num, :, :, :] = xpad[data_num, :,
41+
offset[0]:offset[0] + x.shape[2],
42+
offset[1]:offset[1] + x.shape[2]]
43+
if_flip = np.random.randint(2)
44+
if (if_flip):
45+
x[data_num, :, :, :] = x[data_num, :, :, ::-1]
46+
return x
47+
48+
49+
# Calculate accuracy
50+
def accuracy(pred, target):
51+
# y is network output to be compared with ground truth (int)
52+
y = np.argmax(pred, axis=1)
53+
a = y == target
54+
correct = np.array(a, "int").sum()
55+
return correct
56+
57+
58+
# Data partition according to the rank
59+
def partition(global_rank, world_size, train_x, train_y, val_x, val_y):
60+
# Partition training data
61+
data_per_rank = train_x.shape[0] // world_size
62+
idx_start = global_rank * data_per_rank
63+
idx_end = (global_rank + 1) * data_per_rank
64+
train_x = train_x[idx_start:idx_end]
65+
train_y = train_y[idx_start:idx_end]
66+
67+
# Partition evaluation data
68+
data_per_rank = val_x.shape[0] // world_size
69+
idx_start = global_rank * data_per_rank
70+
idx_end = (global_rank + 1) * data_per_rank
71+
val_x = val_x[idx_start:idx_end]
72+
val_y = val_y[idx_start:idx_end]
73+
return train_x, train_y, val_x, val_y
74+
75+
76+
# Function to all reduce NUMPY accuracy and loss from multiple devices
77+
def reduce_variable(variable, dist_opt, reducer):
78+
reducer.copy_from_numpy(variable)
79+
dist_opt.all_reduce(reducer.data)
80+
dist_opt.wait()
81+
output = tensor.to_numpy(reducer)
82+
return output
83+
84+
85+
def resize_dataset(x, image_size):
86+
num_data = x.shape[0]
87+
dim = x.shape[1]
88+
X = np.zeros(shape=(num_data, dim, image_size, image_size),
89+
dtype=np.float32)
90+
for n in range(0, num_data):
91+
for d in range(0, dim):
92+
X[n, d, :, :] = np.array(Image.fromarray(x[n, d, :, :]).resize(
93+
(image_size, image_size), Image.BILINEAR),
94+
dtype=np.float32)
95+
return X
96+
97+
def run(global_rank,
98+
world_size,
99+
local_rank,
100+
max_epoch,
101+
batch_size,
102+
model,
103+
data,
104+
dir_path,
105+
sgd,
106+
graph,
107+
verbosity,
108+
dist_option='plain',
109+
spars=None,
110+
peft_type='None',
111+
precision='float32',
112+
):
113+
dev = device.get_default_device()
114+
dev.SetRandSeed(0)
115+
np.random.seed(0)
116+
117+
if data == "mnist":
118+
from examples.data import mnist
119+
train_x, train_y, val_x, val_y = mnist.load(dir_path)
120+
else:
121+
raise ValueError(f"`r`Not support dataset {data}")
122+
123+
124+
num_channels = train_x.shape[1]
125+
image_size = train_x.shape[2]
126+
data_size = np.prod(train_x.shape[1:train_x.ndim]).item()
127+
num_classes = (np.max(train_y) + 1).item()
128+
129+
if model == "mlp":
130+
from examples.model import mlp
131+
model = mlp.create_model(in_features=data_size, perceptron_size=16, num_classes=num_classes)
132+
elif model == "cnn":
133+
from examples.model import cnn
134+
model = cnn.create_model(num_channels=num_channels, num_classes=num_classes)
135+
else:
136+
raise ValueError(f"`r`Not support model {model}")
137+
138+
# For distributed training, sequential has better performance
139+
if hasattr(sgd, "communicator"):
140+
DIST = True
141+
sequential = True
142+
else:
143+
DIST = False
144+
sequential = False
145+
146+
if DIST:
147+
train_x, train_y, val_x, val_y = partition(global_rank, world_size,
148+
train_x, train_y, val_x,
149+
val_y)
150+
151+
if model.dimension == 4:
152+
tx = tensor.Tensor(
153+
(batch_size, num_channels, model.input_size, model.input_size), dev, singa_dtype[precision])
154+
elif model.dimension == 2:
155+
tx = tensor.Tensor((batch_size, data_size), dev, singa_dtype[precision])
156+
np.reshape(train_x, (train_x.shape[0], -1))
157+
np.reshape(val_x, (val_x.shape[0], -1))
158+
159+
ty = tensor.Tensor((batch_size,), dev, tensor.int32)
160+
total_train = train_x.shape[0]
161+
num_train_batch = total_train // batch_size
162+
total_val = val_x.shape[0]
163+
num_val_batch = total_val // batch_size
164+
idx = np.arange(total_train, dtype=np.int32)
165+
166+
# Attach model to graph
167+
model.set_optimizer(sgd)
168+
model.compile([tx], is_train=True, use_graph=False, sequential=sequential)
169+
dev.SetVerbosity(verbosity)
170+
171+
# Training and evaluation loop
172+
for epoch in range(max_epoch):
173+
start_time = time.time()
174+
np.random.shuffle(idx)
175+
if global_rank == 0:
176+
print('Starting Epoch %d:' % (epoch))
177+
178+
# Training phase
179+
train_correct = np.zeros(shape=[1], dtype=np.float32)
180+
test_correct = np.zeros(shape=[1], dtype=np.float32)
181+
train_loss = np.zeros(shape=[1], dtype=np.float32)
182+
183+
model.train()
184+
for b in range(num_train_batch):
185+
x = train_x[idx[b * batch_size:(b + 1) * batch_size]]
186+
if model.dimension == 4:
187+
x = augmentation(x, batch_size)
188+
if (image_size != model.input_size):
189+
x = resize_dataset(x, model.input_size)
190+
x = x.astype(np_dtype[precision])
191+
y = train_y[idx[b * batch_size:(b + 1) * batch_size]]
192+
# Copy the patch data into input tensors
193+
tx.copy_from_numpy(x)
194+
ty.copy_from_numpy(y)
195+
# Train the model
196+
out, loss = model(tx, ty, dist_option, spars)
197+
train_correct += accuracy(tensor.to_numpy(out), y)
198+
train_loss += tensor.to_numpy(loss)[0]
199+
200+
if DIST:
201+
# Reduce the evaluation accuracy and loss from multiple devices
202+
reducer = tensor.Tensor((1,), dev, tensor.float32)
203+
train_correct = reduce_variable(train_correct, sgd, reducer)
204+
train_loss = reduce_variable(train_loss, sgd, reducer)
205+
206+
if global_rank == 0:
207+
print('Training loss = %.2f, training accuracy = %.2f %%' %
208+
(train_loss, train_correct / (total_train * world_size) * 100.0), flush=True)
209+
210+
# Evaluation phase
211+
model.eval()
212+
for b in range(num_val_batch):
213+
x = val_x[b * batch_size:(b + 1) * batch_size]
214+
if model.dimension == 4:
215+
if (image_size != model.input_size):
216+
x = resize_dataset(x, model.input_size)
217+
x = x.astype(np_dtype[precision])
218+
y = val_y[b * batch_size:(b + 1) * batch_size]
219+
tx.copy_from_numpy(x)
220+
ty.copy_from_numpy(y)
221+
out_test = model(tx)
222+
test_correct += accuracy(tensor.to_numpy(out_test), y)
223+
224+
if DIST:
225+
# Reduce the evaulation accuracy from multiple devices
226+
test_correct = reduce_variable(test_correct, sgd, reducer)
227+
228+
# Output the evaluation accuracy
229+
if global_rank == 0:
230+
print('Evaluation accuracy = %.2f %%, Elapsed Time = %fs' %
231+
(test_correct / (total_val * world_size) * 100.0, time.time() - start_time), flush=True)
232+
# peft
233+
peft(global_rank, world_size, local_rank, max_epoch, batch_size, model, data, dir_path, sgd, graph, verbosity, dist_option, spars, peft_type, precision)
234+
235+
236+
def peft(global_rank,
237+
world_size,
238+
local_rank,
239+
max_epoch,
240+
batch_size,
241+
model,
242+
data,
243+
dir_path,
244+
sgd,
245+
graph,
246+
verbosity,
247+
dist_option='plain',
248+
spars=None,
249+
peft_type='None',
250+
precision='float32'):
251+
dev = device.get_default_device()
252+
dev.SetRandSeed(0)
253+
np.random.seed(0)
254+
255+
if data == "mnist":
256+
from examples.data import mnist
257+
train_x, train_y, val_x, val_y = mnist.load(dir_path)
258+
else:
259+
raise ValueError(f"`r`Not support dataset {data}")
260+
261+
num_channels = train_x.shape[1]
262+
image_size = train_x.shape[2]
263+
data_size = np.prod(train_x.shape[1:train_x.ndim]).item()
264+
num_classes = (np.max(train_y) + 1).item()
265+
266+
# For distributed training, sequential has better performance
267+
if hasattr(sgd, "communicator"):
268+
DIST = True
269+
sequential = True
270+
else:
271+
DIST = False
272+
sequential = False
273+
274+
if DIST:
275+
train_x, train_y, val_x, val_y = partition(global_rank, world_size,
276+
train_x, train_y, val_x,
277+
val_y)
278+
279+
if model.dimension == 4:
280+
tx = tensor.Tensor(
281+
(batch_size, num_channels, model.input_size, model.input_size), dev, singa_dtype[precision])
282+
elif model.dimension == 2:
283+
tx = tensor.Tensor((batch_size, data_size), dev, singa_dtype[precision])
284+
np.reshape(train_x, (train_x.shape[0], -1))
285+
np.reshape(val_x, (val_x.shape[0], -1))
286+
287+
ty = tensor.Tensor((batch_size,), dev, tensor.int32)
288+
total_train = train_x.shape[0]
289+
num_train_batch = total_train // batch_size
290+
total_val = val_x.shape[0]
291+
num_val_batch = total_val // batch_size
292+
idx = np.arange(total_train, dtype=np.int32)
293+
# Attach model to graph
294+
peft_model = model
295+
# peft
296+
if peft_type == 'linear_lora':
297+
config = LinearLoraConfig(8, 1, 0., ["linear1", "linear2"])
298+
peft_model = get_peft_model(model, config)
299+
peft_model.set_optimizer(sgd)
300+
peft_model.compile([tx], is_train=True, use_graph=graph, sequential=sequential)
301+
dev.SetVerbosity(verbosity)
302+
303+
# Training and evaluation loop
304+
for epoch in range(max_epoch):
305+
start_time = time.time()
306+
np.random.shuffle(idx)
307+
if global_rank == 0:
308+
print('Starting Epoch %d:' % (epoch))
309+
310+
# Training phase
311+
train_correct = np.zeros(shape=[1], dtype=np.float32)
312+
test_correct = np.zeros(shape=[1], dtype=np.float32)
313+
train_loss = np.zeros(shape=[1], dtype=np.float32)
314+
315+
peft_model.train()
316+
for b in range(num_train_batch):
317+
x = train_x[idx[b * batch_size:(b + 1) * batch_size]]
318+
if model.dimension == 4:
319+
x = augmentation(x, batch_size)
320+
if (image_size != model.input_size):
321+
x = resize_dataset(x, model.input_size)
322+
x = x.astype(np_dtype[precision])
323+
y = train_y[idx[b * batch_size:(b + 1) * batch_size]]
324+
# Copy the patch data into input tensors
325+
tx.copy_from_numpy(x)
326+
ty.copy_from_numpy(y)
327+
# Train the model
328+
out, loss = peft_model(tx, ty, dist_option, spars)
329+
train_correct += accuracy(tensor.to_numpy(out), y)
330+
train_loss += tensor.to_numpy(loss)[0]
331+
332+
if DIST:
333+
# Reduce the evaluation accuracy and loss from multiple devices
334+
reducer = tensor.Tensor((1,), dev, tensor.float32)
335+
train_correct = reduce_variable(train_correct, sgd, reducer)
336+
train_loss = reduce_variable(train_loss, sgd, reducer)
337+
338+
if global_rank == 0:
339+
print('Training loss = %.2f, training accuracy = %.2f %%' %
340+
(train_loss, train_correct / (total_train * world_size) * 100.0), flush=True)
341+
# Evaluation phase
342+
peft_model.eval()
343+
for b in range(num_val_batch):
344+
x = val_x[b * batch_size:(b + 1) * batch_size]
345+
if model.dimension == 4:
346+
if (image_size != model.input_size):
347+
x = resize_dataset(x, model.input_size)
348+
x = x.astype(np_dtype[precision])
349+
y = val_y[b * batch_size:(b + 1) * batch_size]
350+
tx.copy_from_numpy(x)
351+
ty.copy_from_numpy(y)
352+
out_test = peft_model(tx)
353+
test_correct += accuracy(tensor.to_numpy(out_test), y)
354+
if DIST:
355+
# Reduce the evaulation accuracy from multiple devices
356+
test_correct = reduce_variable(test_correct, sgd, reducer)
357+
358+
# Output the evaluation accuracy
359+
if global_rank == 0:
360+
print('Evaluation accuracy = %.2f %%, Elapsed Time = %fs' %
361+
(test_correct / (total_val * world_size) * 100.0, time.time() - start_time), flush=True)
362+
363+
# for infer, merge_weights can speed up
364+
peft_model.merge_weights(mode=True)
365+
print("after merge weights.")
366+
print(peft_model.get_params())
367+
# for train or val, unmerge weights to train
368+
peft_model.merge_weights(mode=False)
369+
print("after unmerge weights.")
370+
print(peft_model.get_params())
371+
372+
373+
if __name__ == '__main__':
374+
parser = argparse.ArgumentParser(
375+
description='Training using the autograd and graph.')
376+
parser.add_argument('model', choices=['mlp', 'cnn'], default='mlp')
377+
parser.add_argument('data', choices=['mnist'], default='mnist')
378+
parser.add_argument('peft', choices=['None', "linear_lora"], default='None')
379+
parser.add_argument('-m', '--max-epoch', default=100, type=int, help='maximum epochs', dest='max_epoch')
380+
parser.add_argument('-dir', '--dir-path', default="/tmp/mnist", type=str, help='the directory to store the malaria dataset', dest='dir_path')
381+
parser.add_argument('-b', '--batch-size', default=32, type=int, help='batch size', dest='batch_size')
382+
parser.add_argument('-l', '--learning-rate', default=0.01, type=float, help='initial learning rate', dest='lr')
383+
parser.add_argument('-i', '--device-id', default=0, type=int, help='which GPU to use', dest='device_id')
384+
parser.add_argument('-g', '--disable-graph', default='True', action='store_false', help='disable graph', dest='graph')
385+
parser.add_argument('-v', '--log-verbosity', default=0, type=int, help='logging verbosity', dest='verbosity')
386+
387+
args = parser.parse_args()
388+
sgd = opt.SGD(lr=args.lr, momentum=0.9, weight_decay=1e-5, dtype=singa_dtype[args.precision])
389+
run(0, 1, args.device_id,
390+
args.max_epoch,
391+
args.batch_size,
392+
args.model,
393+
args.data,
394+
args.dir_path,
395+
sgd,
396+
args.graph,
397+
args.verbosity,
398+
peft_type=args.peft,
399+
precision=args.precision)

0 commit comments

Comments
 (0)