텐서플로우 GPU병렬성
텐서 플로우의 병렬성은
- 모델 병렬성 : 주요 원칙은 모델 중의 다른 계산 노드를 서로 다른 하드웨어 리소스에 배치하여 연산
- 데이터 병렬성 : 대규모 병렬 처리를 구현하기 위해 일반적으로 사용한다.
으로 구분된다.
아래의 명령어로 데이터를 미리 준비해두자.
$ git clone https://github.com/tensorflow/models.git
다운 받은 폴더에 models/tutorials/image/cifar10에 가면 cifar10_multi_gpu_train.py라는 파일이 있다. 이 파일을 실행 시켜주면 된다!
(tensorEnv) weejiwon@weeserver:~/tensorEnv/exampleCode/models/tutorials/image/cifar10$ ls
BUILD cifar10_input_test.py cifar10_train.py
cifar10_eval.py cifar10_multi_gpu_train.py __init__.py
cifar10_input.py cifar10.py README.md
(tensorEnv) weejiwon@weeserver:~/tensorEnv/exampleCode/models/tutorials/image/cifar10$ vi cifar10_multi_gpu_train.py
(tensorEnv) weejiwon@weeserver:~/tensorEnv/exampleCode/models/tutorials/image/cifar10$ python3 cifar10_multi_gpu_train.py
>> Downloading cifar-10-binary.tar.gz 100.0%
Successfully downloaded cifar-10-binary.tar.gz 170052171 bytes.
그럼 아래와 같이...훈련이 진행된다.
2019-02-20 10:10:28.450461: step 1260, loss = 2.17 (151.4 examples/sec; 0.845 sec/batch)
2019-02-20 10:10:36.809246: step 1270, loss = 2.16 (155.7 examples/sec; 0.822 sec/batch)
2019-02-20 10:10:45.144759: step 1280, loss = 2.09 (154.3 examples/sec; 0.830 sec/batch)
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 | def tower_loss(scope): images, labels = cifar10.distorted_inputs() logits = cifar10.inference(images) _ = cifar10.loss(logits, labels) losses = tf.get_collection('losses', scope) total_loss = tf.add_n(losses, name='total_loss') loss_averages = tf.train.ExponentialMovingAverage(0.9, name='avg') loss_averages_op = loss_averages.apply(losses + [total_loss]) for l in losses + [total_loss]: loss_name = re.sub('%s_[0-9]*/' % cifar10.TOWER_NAME, '', l.op.name) tf.scalar_summary(loss_name +' (raw)', l) tf.scalar_summary(loss_name, loss_averages.average(l)) with tf.control_dependencies([loss_averages_op]): total_loss = tf.identity(total_loss) return total_loss | cs |
1) line2에서 distorted_inputs()을 사용하여 데이터가 왜곡된 이미지 및 레이블을 생성
2) line4에서 컨벌루션 신경망을 생성한다.
3) 손실함수를 계산하고 마지막에 total_loss를 tf.add_n 함수를 이용하여 모든 손실을 합한다.
2. 서로 다른 GPU로 계산된 기울기를 합성하는 함수
tower_grads는 기울기의 한쌍의 레이어 목록이다. tuple(기울기,변수)로 이루어짐 ex [[(grad0_gpu0,var_gpu0),(grad1_gpu1,var1_gpu0)]]이렇게
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def average_gradients(tower_grads): average_grads = [] for grad_and_vars in zip(*tower_grads): grads = [] for g, _ in grad_and_vars: expanded_g = tf.expand_dims(g, 0) grads.append(expanded_g) grad = tf.concat(0, grads) grad = tf.reduce_mean(grad, 0) v = grad_and_vars[0][1] grad_and_var = (grad, v) average_grads.append(grad_and_var) return average_grads | cs |
1) line3에서 zip을 수행하면 각 gpu에서 계산한 var끼리 뭉쳐진다 [[(gpu0,var1),(gpu1,var1)],[(gpu0,var0),(gpu1,var0)]] 요롷게
2) line6에서 expand_dims 함수를 사용하면 기울기에 중복된 차원 0을 추가한다.
3) line10에서 추가한 차원 0에 기울기를 병합한다음에
4) line11에서 reduce_mean을 이용해 차원-0의 평균을 구한다.
5) 마지막에 평균 기울기를 Variable과 결합해서 원래 튜플 형식으로 변환한다. (..?흠...그냥 하나로 병합해서 평균구하고 다시 뿌리는 작업을 하는듯)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | def train(): with tf.Graph().as_default(), tf.device('/cpu:0'): global_step = tf.get_variable('global_step', [], initializer=tf.constant_initializer(0), trainable=False) num_batches_per_epoch = (cifar10.NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN / FLAGS.batch_size) decay_steps = int(num_batches_per_epoch * cifar10.NUM_EPOCHS_PER_DECAY) lr = tf.train.exponential_decay(cifar10.INITIAL_LEARNING_RATE, global_step, decay_steps, cifar10.LEARNING_RATE_DECAY_FACTOR, staircase=True) opt = tf.train.GradientDescentOptimizer(lr) |
아래의 4가지를 설정하는 부분이다.
1) global_step : 훈련 단계 수를 기록한다.
2) epoch : batch 수 설정
3) decay : exponetial_decay(초기 학습속도, 전역 훈련의 단계, 각 감쇠에 필요한 단계, 감쇠율, True로 설정된것은 단계적 감쇠를 뜻함)
4) opt : 최적화 알고리즘은 GradientDescent로 설정
4. GPU 계산 결과 저장
1 2 3 4 5 6 7 8 9 10 11 | tower_grads = [] for i in xrange(FLAGS.num_gpus): with tf.device('/gpu:%d' % i): with tf.name_scope('%s_%d' % (cifar10.TOWER_NAME, i)) as scope: loss = tower_loss(scope) tf.get_variable_scope().reuse_variables() summaries = tf.get_collection(tf.GraphKeys.SUMMARIES, scope) grads = opt.compute_gradients(loss) tower_grads.append(grads) grads = average_gradients(tower_grads) apply_gradient_op = opt.apply_gradients(grads, global_step=global_step) | cs |
1) GPU 개수만큼 loop를 돌게 한다
2) tf.device로 gpu 선택한 뒤
3) line 6에서 함수를 이용하여 모든 GPU가 하나의 모델 및 동일한 매개 변수를 공유토록 한다.
4) line 8에서 단일 gpu의 기울기를 계산해서 line 1에 있는 변수에 추가한다.
5) line 10에서 평균을 계산해서
6) line 11에서 모델 매개변수를 업데이트 한다.