Keras Dense 레이어: 정확하게 사용하는 방법
이 글에서는 Keras의 Dense 레이어를 살펴봄으로써 Keras에서 사용자 지정 모델을 구축할 때 중요한 내용을 완벽하게 이해할 수 있게 합니다.
Created on March 28|Last edited on May 30
Comment
Keras의 Dense 레이어는 오래되고 훌륭한 완전하고/밀도있게 연결된 신경망입니다. 이보다 나은 것은 없습니다! 하지만, Keras에서 사용자 지정 모델을 구축하는 동안 이것을 완전하게 이해하는 데에는 오랜 시간이 걸립니다.
이 글에서 Dense 레이어란 무엇이고 실제 어떻게 작동하는지에 대해 알아봄으로써 필요한 모든 것을 갖출 수 있게 됩니다.
이 글에서 다룰 내용은 다음과 같습니다:
목차
Dense 레이어란?
머신 러닝에서, 완전하게 연결된 레이어는 각 입력 기능을 레이어 내 각 뉴런에 연결시킵니다. Dense 레이어는 기능 추출 블록(합성곱 또는 인코더, 디코더 등), 출력 레이어(최종 레이어) 뒤의 끝에서 두 번째 레이어로, 또한 차원 d0의 벡터를 새로운 차원 d1로 투사하는 데 가장 많이 사용됩니다.
1D 입력 기능에 대해 살펴보겠습니다:
이 입력은 4개의 뉴런으로 완전하게 연결된 레이어를 사용하여 처리됩니다(우리는 단순화를 위해 선형성과 편향은 무시합니다). 예를 들어 3 * 4 = 12를 얻기 위해 몇 개를 연결해야 합니까? 아래 그림에서 보듯, 1D 특징 공간 내 각 값에 4 가중치 벡터(색상 선으로 표시됨)를 곱합니다.

그림1: 완전 연결 신경망.
코드를 보여주세요
우리는 Keras를 사용하여 그림 1에서 보는 완전 연결 신경망을 실행할 것입니다. Dense 레이어는 아래에서 보듯, 이를 달성하기 위해 사용할 수 있습니다:
import tensorflow as tffrom tensorflow.keras import layers# batch_size가 1인 무작위 입력 특징 모양을 만듭니다.inputs = tf.random.uniform(shape=(1,3)) # (batch_size, num_features)# 완전하게 연결된 레이어를 초기화합니다(단순화를 위해 편향을 사용하지 않을 것입니다).dense_layer = layers.Dense(unit=4, use_bias=False)# 출력으로 무엇을 기대할 수 있나요?print(dense_layer(inputs))>>> tf.Tensor([[-0.07313907 0.31886736 -0.83136106 0.47411117]], shape=(1, 4), dtype=float32)
모양 (1,3)의 입력에 대해, 4개의 뉴런이 완전하게 연결된 레이어 안에 있기 때문에 모양 (1,4)의 출력을 얻습니다. 그림 1에서, 12개의 선은 무게를 나타내기 위해 그려졌습니다. 완전하게 연결된 레이어의 시작 무게를 검사해 봅시다:
print(dense_layer.weights)>>> [<tf.Variable 'dense_1/kernel:0' shape=(3, 4) dtype=float32, numpy=array([[-0.18026423, 0.8457761 , 0.20618927, 0.34542954],[-0.68638337, -0.09881872, -0.891773 , 0.7983763 ],[ 0.84850013, -0.5968083 , -0.522443 , -0.62690055]],dtype=float32)>]
예상했듯이, 이것은 모양의 척도입니다 (3, 4) (3*4 = 12).
N차원 입력은 어떠세요?
이제 모양(time/num_frame/arbitrary_feature, features)의 입력을 가지고 있다고 가정해 봅니다. 이 입력은 시퀀스(시계열 또는 비디오) 또는 임의적 특징 공간일 수 있습니다. 사용 사례에 따라, 이 입력을 완전하게 연결된 레이어를 통해 복수의 방식으로 전달(또는 처리)할 수 있습니다. 이상한 상황에 빠지기 전에 몇 가지 고려해 봅시다.
1. 이는 임의적 특징 공간입니다
입력은 모양(arbitrary_feature, features)의 임의 특징 공간일 수 있습니다. 입력 평탄화를 고려하고 실험할 수 있습니다. Keras에서 입력 평탄은 어떤 모습일까요? 같이 찾아 보겠습니다!
1.1 입력 평탄화
Keras에서 모든 입력을 1D 벡터로 평탄화하기 위해 Flatten() 레이어를 사용할 수 있습니다. 이 레이어는 예를 들어, 입력에 배치 크기가 32인 모양 (32, 2, 3)이 있으면, 배치 차원을 따라 평탄화되지 않습니다. 평탄 작업은 모양(32, 6)의 벡터를 제공할 것입니다.
# 1이 배치 크기인 모양(1, 2, 3)의 상수 입력을 초기화합니다.inputs = tf.constant([[[1, 1, 1], [2, 2, 2]]])# 평탄 레이어 초기화flatten = layers.Flatten()# 출력으로 무엇을 기대할 수 있나요?outputs = flatten(inputs)print(outputs)>>> tf.Tensor([[1 1 1 2 2 2]], shape=(1, 6), dtype=int32)
배치 크기가 1인 모양이 평탄화된 벡터 (1, 6) 를 얻습니다. 이 레이어는 심층 신경망 내 특징 추출 블럭 다음에 일반적으로 사용됩니다. 이것은 또한 arbitrary_feature 차원이 독립적인 경우, 사용하기에 유효하며, 시퀀스의 일부가 아닌 features를 고려할 수 있습니다. 이것을 Dense 레이어를 통해 전달해 보겠습니다:
dense_layer = layers.Dense(units=4, # 이 완전하게 연결된 레이어 안에 4개의 뉴런이 있습니다.use_bias=False,kernel_initializer=tf.keras.initializers.Constant(value=0.5) # 가중치는 상수값 0.5로 초기화됩니다.)# 출력으로 무엇을 기대할 수 있나요?print(dense_layer(outputs))>>> tf.Tensor([[4.5 4.5 4.5 4.5]], shape=(1, 4), dtype=float32)
이 값을 어떻게 얻었습니까? 완전하게 연결된 레이어 다음, 각 출력 값 (4.5)은 1*0.5 + 1*0.5 + 1*0.5 + 2*0.5 + 2*0.5 + 2*0.5 = 4.5와 같이 계산됩니다.
2. 이것은 시퀀스입니다
이제, 모양 입력(시간, 특징)을 고려해 봅니다. 여기 각 기능은 시간 축을 따라 종속되며, 평평한 벡터는 이 종속성을 잃습니다. 우리가 특징 의 차원을 새 차원으로 투영하는 시나리오를 생각해 보십시오. 이렇게 하기 위해 Keras Dense() 레이어를 사용할 수 있나요?
레이어에 입력에 2보다 큰 순위가 있으면, Dense는 입력의 마지막 축 및 커널의 0축을 따라 입력과 커널 사이의 도트 곱을 계산합니다. 예를 들어, 입력에 차원(batch_size, d0, d1)이 있다면, 우리는 모양 (d1, units)으로 커널을 만들고, 커널은 모양 (1, 1, d1)의 모든 하위 tensor에서 입력의 2축을 따라 작동합니다(batch_size * d0의 하위 tensor가 있습니다). 이 사례에서 출력은 모양(batch_size, d0, 단위)을 갖게 됩니다.
이것을 나눠 코드로 각각의 이동 부분를 이해해 보겠습니다.
# 입력은 배치 크기는 1이고 시간 축은 2인 모양(1, 2, 3)입니다.inputs = tf.constant([[[1, 1, 1], [2, 2, 2]]])# 입력의 순위가 2 이상입니까?print(tf.rank(inputs))>>> <tf.Tensor: shape=(), dtype=int32, numpy=3>
Dense 레이어를 사용하여 3부터 4까지 마지막 차원을 투영해 보도록 하겠습니다.
# Dense 레이어를 4개의 출력 뉴런과 상수 가중치 0.5로 초기화합니다.dense_layer = layers.Dense(units=4,use_bias=False,kernel_initializer=tf.keras.initializers.Constant(value=0.5))# 예측되는 출력은 무엇이어야 합니까?print(dense_layer(inputs))>>> tf.Tensor([[[1.5 1.5 1.5 1.5][3. 3. 3. 3. ]]], shape=(1, 2, 4), dtype=float32)
보시다시피, Dense 레이어는 모양 (1, 2, 3) 에서 (1, 2, 4)까지의 입력을 투영합니다. 이 계산때문에 1.5의 출력값을 얻었습니다: 1*0.5 + 1*0.5 + 1*0.5 = 1.5. 이와 유사하게, 이 계산때문에 3.0의 출력값을 얻었습니다: 2*0.5 + 2*0.5 + 2*0.5 = 3. 문서화에 따라, 비중 척도의 모양은 (3, 4)이어야 합니다.
print(dense_layer.weights)>>> [<tf.Variable 'dense_5/kernel:0' shape=(3, 4) dtype=float32, numpy=array([[0.5, 0.5, 0.5, 0.5],[0.5, 0.5, 0.5, 0.5],[0.5, 0.5, 0.5, 0.5]], dtype=float32)>]
2.1 TimeDistributed 레이어
우리는 우리의 (time, features) 입력을 다양한 차원에 투영하는 관점에서 보았지만, Dense 작업(도트 곱)을 각 기능에 순차적으로 적용하기를 원하면 어떻게 될까요?
이 논의에 좀 더 많은 뉘앙스를 추가하려면, 모양 (num_frames, height, width, 3)의 비디오 데이터 샘플을 상상해 보십시오. 사전에 학습된 이미지 모델을 사용하여 각 프레임에서 순차적으로 정보를 추출하고 싶어할 겁니다. TimeDistributed 레이어를 통해 레이어(여기에서 추출기 기능)를 입력(여기서 비디오)의 모든 일시적 슬라이스(여기서 프레임)에 적용할 수 있습니다.
Dense 연산을 모양 (time, features)의 입력에 어떻게 적용할 수 있는지를 살펴보겠습니다. 이전의 예에서 inputs를 사용하겠습니다:
# 이 dense 레이어는 매 ‘시간’마다 적용됩니다.dense_layer = layers.Dense(units=4,use_bias=False,kernel_initializer=tf.keras.initializers.Constant(value=0.5))# `TimeDistributed` 레이어를 초기화하세요.timedistributed = layers.TimeDistributed(dense_layer)# 예측되는 출력은 무엇이어야 합니까?print(timedistributed(inputs))>>> tf.Tensor([[[1.5 1.5 1.5 1.5][3. 3. 3. 3. ]]], shape=(1, 2, 4), dtype=float32)
실습
좋아요! 이제 Keras Dense 레이어를 정확하게 사용하는 법을 알았습니다. 그러면 이 레이어로 실험하여 더 자세히 알아보도록 하겠습니다. 단순하게 한다음 Scikit Learn의 make_classification을 이용하여 만든 합성 데이터 집합을 이용해보겠습니다. 데이터 집합에는 10가지의 기능과 10가지의 클래스가 있게 됩니다. 이 기능들은 신경망이 어떠한 도움 없이도 기능을 얼마나 잘 추출할 수 있는 지를 보기 위해 정상화되지 않습니다.
데이터 집합은 10000가지의 샘플로 일정하게 유지됩니다. 우리의 모델을 배치 크기 256으로 100개의 에포크에 대해 학습시키겠습니다.
Colab 메모장을 확인하세요
완전 연결 신경망은 다음과 같아 보입니다:
def MLPModel():inputs = layers.Input(shape=(10,))x = layers.Dense(configs["hidden_units"], activation="relu")(inputs)outputs = layers.Dense(10, activation="softmax")(x)return models.Model(inputs, outputs)
파라미터의 효과 - units
Dense 레이어의 첫 번째 인수는 해당 레이어의 뉴런 수를 제어할 수 있는 units입니다. 단위의 수는 10, 100 또는 1000이 됩니다. 아래 패널은 다른 단위를 사용한 결과를 보여줍니다.
명확하게, 파라미터(units)의 수를 늘림으로써 모델이 개선됩니다.
학습과 검증 손실을 살펴보면, 단위가 1000인 것은 빠르게 과적합되는 반면, 10개의 뉴런만 있는 것은 아직 발산을 시작하지도 않았습니다.
커널 정규화의 효과
해당 모델의 역량을 낮추지 않고도 이 과적합을 계산할 수 있을까요? 커널 정규화를 들어가면, 최적화 중에 레이어 파라미터 페널티를 적용할 수 있게 됩니다. 세부 사항은 살펴보지 않으며 동작의 효과를 보겠습니다.
어떠한 정규화없이 1000개의 숨겨진 단위로 학습한 모델과 동일한 구성이지만 kernel_regularization으로 학습한 모델을 비교할 것입니다. l2 인수를 취한L2 정규화 페널티를 사용합니다. 이는 [0-1] 범위의 플로팅 값입니다. 0에 가까운 값은 정규화가 없다는 의미입니다.
이전 섹션에서, 정규화가 없이 학습된 모델은 금방 과적합되기 시작했음을 확인했습니다. 다른 l2 값과 전체적 정규화의 효과를 보겠습니다.
no-reg 실행에 대해 loss와 val_loss 사이의 발산에 주목하십시오. 과적합을 나타내는 가장 큰 발산이 있는 반면, reg-0.1 실행은 가장 적게 발산했습니다. 정규화는 모델의 최종 정확도에 어떻게 영향을 미칠까요?
l2 값이 0.1인 L2 정규화는 비록 이것이 과적합의 카운터에 도움을 주었지만, 모델이 val_acc의 동일한 수준을 달성하는 데는 도움을 주지 못했습니다. 하지만, 따라서 더 오랜 시간 동안 정규화된 모델을 학습시킬 수 있습니다. 실제, 우리는 보통 l2 값 1e-3으로 정규화합니다.
결론
이 짧은 리포트는 완전하게 연결된 레이어가 Keras를 사용하여 어떻게 초기화되고 생성될 수 있는지를 보여줍니다. 또한 그 사용을 더 잘 이해하기 위해 비교하는 연구를 일부 수행했습니다.
Dense 레이어는 딥 러닝에서 기본 레이어이며 보통 어텐션 레이어, MLP 블록, 프로젝터 등에 사용됩니다. dense를 연결하면, 파라미터의 수는 높아지므로, 이미지와 같이 고차원적인 입력을 직접 처리하는 데에는 이상적이지 않습니다.
더 복잡한 사용 사례가 있다면, EinsumDense 레이어를 대신 사용할 것을 고려해 보십시오. 이는 einsum 식을 사용하며, Dense는 EinsumDense의 특별 케이스입니다. 이것은 다른 리포트에서 곧 다루도록 하겠습니다!
Add a comment
Iterate on AI agents and models faster. Try Weights & Biases today.