본문 바로가기
Study/AI

[AI] TensorFlow 기초 개념과 활용

by ngool 2025. 2. 6.

TensorFlow는 Google이 개발한 오픈소스 머신러닝 라이브러리로, 딥러닝 모델을 효율적으로 구축하고 학습시키는 데 최적화되어 있습니다.

본 포스팅에서는 TensorFlow의 핵심 개념과 함께 간단한 예제를 통해 기본적인 사용법을 알아보겠습니다.


TensorFlow 시작하기

TensorFlow를 사용하기 위해 먼저 라이브러리를 설치하고 버전을 확인해 봅니다.

import tensorflow as tf
print(tf.__version__)	# 2.18.0

 

저는 2.18.0 버전을 사용하고 있습니다.


텐서(Tensor)의 객체

  • 타입(Type) : string, float32, float16, int32, int8 등
    • 딥러닝의 경우 숫자의 경향만 표현하면 되기 때문에 계산량을 줄이고 빠르게 계산하기 위해 일반적으로 float32(더 정교하게 숫자 표현)보다 float16(덜 정교하게 숫자 표현)을 사용
  • 형상(Shape) : 0, 1, 2차원 등의 데이터 차원
  • 축(Rank) : 차원의 개수

텐서의 차원

텐서 객체는 constant()라는 함수로 만들 수 있고, 차원의 개수는 rank()로 확인할 수 있습니다.

a = tf.constant(2)
print(tf.rank(a))   # 0차원
print(a)            # 2

b  = tf.constant([2,3])
print(tf.rank(b))   # 1차원
print(b)            # [2 3]

c = tf.constant([[2,3],[6,7]])
print(tf.rank(c))   # 2차원
print(c)            # [[2 3] [6 7]]


텐서의 연산

당연히 사칙연산 모두 가능합니다.

먼저 텐서 객체를 만들어봅시다.

a = tf.constant(3)
b = tf.constant(2)

 

이제 연산을 한번 해보죠.

연산은 텐서플로우의 add, subtract, multiply, divide 함수를 사용해서 할 수 있고, 파이썬의 연산자로도 연산이 가능합니다.

print(tf.add(a,b))
print(a+b)

print(tf.subtract(a,b))
print(a-b)

print(tf.multiply(a,b))
print(a*b)

print(tf.divide(a,b))
print(a/b)


텐서 플로우 넘파이

TensorFlow 2.x에서는 Eager Execution(즉시 실행 모드) 기능을 지원하는데, 이로써 넘파이 함수를 이용하여 텐서를 계산할 수 있게 되었습니다.

이 작업에 필수적인 2가지 함수를 확인해봅시다.

  • numpy() : 넘파이 배열로 만들기
  • tf.convert_to_tensor() : 텐서 형태로 만들기

먼저 넘파이 배열을 한번 만들어 봅시다.

c = (5).numpy()

print(c)	# 5
print(type(c))	# <class 'numpy.int32'>

 

넘파이가 되었으니 여러가지 계산이 가능해졌습니다.

루트를 한번 씌워볼까요? 데이터 타입은 float32로 설정해주겠습니다.

그런 다음 다시 텐서 형태로 바꾸면 어떻게 될까요?

c_sqrt = np.sqrt(c ,dtype=np.float32)
c_tensor = tf.convert_to_tensor(c_sqrt)

print(c_tensor)
print(type(c_tensor))

 

넘파이 값 그대로 텐서 객체가 되었습니다.

텐서 객체를 살펴보니 EagerTensor라는 말이 붙어 있는 것도 확인해볼 수 있네요!


넘파이처럼 사용하기

그런데 애초에 텐서 객체를 넘파이처럼 사용하는 것도 가능합니다.

 

2차원 텐서 객체를 만들어보겠습니다.

t = tf.constant([[1.,2.,3.],[4.,5.,6.]])
print(t.shape)  # (2, 3)
print(t.dtype)  # <dtype: 'float32'>

 

넘파이에서 가능한 슬라이싱이 여기서도 바로 될까요?

행은 그대로 두고 1열을 없애보겠습니다.

print(t[:,1:])

와.. 이게 됩니다.

 

그럼 행렬의 모든 값에 같은 값을 더해주는 연산도 될까요?

t + 10

이게 되네요.. 굉장히 직관적인 코드이니 요긴하게 사용될 수 있을 것 같습니다.

 

그럼 행렬곱도 가능할까요?

간단하게 t 행렬과 t의 전치행렬을 곱해보겠습니다.

# 행렬곱
t @ tf.transpose(t)

문제없이 작동합니다. 왠만한 행렬 연산은 다 되는 것 같군요.


텐서의 타입 변환

  • 텐서의 기본 dtype
    • float형 텐서 : float32
    • int형 텐서 : int32
  • 타입 변환 시 사용하는 함수
    • tf.cast()

타입변환은 왜 필요할까요?

그 이유는 텐서의 경우 연산 시 타입이 맞지 않으면 에러가 발생하기 때문입니다.

a = tf.constant(2)  # dtype=int32
b = tf.constant(2.) # dtype=float32
a+b

 

int ↔ float 뿐만 아니라, float32 ↔ float64도 안됩니다.

tf.constant(2.) + tf.constant(2., dtype=tf.float64)

 

자 그럼 이제 cast() 함수를 사용해서 데이터 타입을 맞춰준 뒤 연산해봅시다.

t = tf.constant(2, )    # int32
t2 = tf.constant(2.)    # float32

t3 = t + tf.cast(t2, tf.int32)
print(t3)
print(type(t3))

t = tf.constant(2.0, dtype=tf.float64)  # float64
t2 = tf.constant(2.)    # float32

t3 = t + tf.cast(t2, tf.float64)
print(t3)
print(type(t3))


난수 생성

텐서플로우에서는 특정 조건 내에서 랜덤한 값도 생성할 수 있습니다.

rand1 = tf.random.uniform([1], 0, 1)
print(rand1.shape)	# (1,)
print(rand1)	# tf.Tensor([0.45497942], shape=(1,), dtype=float32)
  • [1] → 크기가 1인 텐서(1차원 배열)를 생성
  • 0, 1 → 0 이상, 1 미만의 균등 분포(Uniform Distribution)에서 숫자를 랜덤하게 뽑음
rand2 = tf.random.normal(shape=(1,2), mean=0, stddev=1)
print(rand2.shape)	# (1,2)
print(rand2)	tf.Tensor([[ 0.43970278 -0.18841505]], shape=(1, 2), dtype=float32)

 

  • shape=(1,2) 크기가 (1행, 2열)인 2D 텐서(행렬) 생성
  • mean=0, stddev=1 → 평균이 0, 표준편차가 1인 정규분포(가우시안 분포)에서 숫자를 랜덤하게 뽑음

 

rand3 = tf.random.normal([3,2], 0, 1)
print(rand3.shape)	# (3, 2)
print(rand3)

이렇게 옵션 명칭을 생략하고 쓸 수도 있습니다.

간편하지만 가독성 면에서는 좋지 않으니 상황에 맞게 사용하는 것이 좋겠네요!