Python/Tensorflow, Keras

1) 텐서플로우(Tensorflow)

sundori 2023. 12. 25. 20:52

목차

    텐서플로우

    텐서플로는 구글의 어느 한 팀에 의해 공개된 대표적인 머신러닝 라이브러리이다.
    이는 파이썬뿐만 아니라, 다양한 언어를 사용하고 모델을 개발하고 배포할 수 있는 다양한 도구를 지원한다.

    그리고 텐서플로우는 딥러닝 연산을 처리하는 라이브러리로서 텐서라고 불리는 데이터를 계산 그래프 구조를 통해 흘려가면서 복잡한 행렬 연산을 처리하게 된다. 이번에는 파이썬 언어를 이용해 공부해보자 한다.

    텐서플로우 자료구조

    텐서플로우는 파이썬 자료형 값들을 텐서플로우 자료구조인 텐서(Tensor)로 변환되어 처리하는데..

    0차원 텐서인 스칼라, 1차원 벡터, 2차원 행렬, 3차원 텐서, 4차원 텐서..... 차수가 1씩 증가함에 따라 데이터 구조가 확장된다.

    여기서 차수는 차원의 수를 말하는 것이며 텐서를 구성하는 벡터의 개수를 나타낸다. 밑에 그림을 보면 그나마 이해가 될 것이다.

    텐서의 종류

    스칼라(Scalar)

    스칼라는 정수나 실수와 같은 상수(Constant Number)를 나타낸다고들 하는데 그 이유는 양을 나타낼 수는 있지만 방향성을 가지지 않기 때문이다. 방향성이 존재하지 않는다는 소리는 벡터가 존재하지 않는다는 소리이다. 따라서 벡터가 존재하지 않기 때문에 차수가 0이 되며 텐서플로우에서는 '랭크-0' 텐서라고 부른다.

    # 텐서플로우 불러오기
    import tensorflow as tf
    
    # 스칼라 정의해보기
    a = tf.constant(1)
    b = tf.constant(2)
    print("a: ", a)
    print("b: ", b)
    ---------------------------------------
    a:  tf.Tensor(1, shape=(), dtype=int32)
    b:  tf.Tensor(2, shape=(), dtype=int32)

    위의 코드처럼 스칼라 텐서는 constant 함수에 상수 값을 입력하여 만드는데 변수 a에는 스칼라 1을 변수 b에는 스칼라 2를 할당하여 이를 출력한다. 출력 결과를 보면 a:  tf.Tensor(1, shape=(), dtype=int32) b:  tf.Tensor(2, shape=(), dtype=int32)처럼 텐서 자료구조로 변환된 것을 볼 수 있다.

     

    tf.Tensor(상수 값, 배열의 크기, 저장된 값의 자료형 int32 = 32비트 정수형)인데 shape=()은 배열을 나타내는 값이 존재하지 않아 0차원이다.

    랭크 확인하기

    rank함수를 사용하면 텐서 객체의 랭크(차수)를 알 수 있다. 변수 a에 저장된 텐서의 랭크를 출력하면 값이 0이 나온다.

    print(tf.rank(a))
    ----------------------------------
    tf.Tensor(0, shape=(), dtype=int32)

    텐서 자료형 변환

    텐서 자료형을 변환을 하고 싶을 때 cast 함수를 사용한다. a변수는 32비트 정수형을 나타내는 int32로 저장되어 있는데 이를 32비트 실수형을 나타내는 float32로 변환해 본다.

    a = tf.cast(a, tf.float32)
    
    print(a)
    ---------------------------------------
    tf.Tensor(1.0, shape=(), dtype=float32)

    math 모듈

    math 모듈에는 여러 가지 수학 함수가 정의되어 있는데 텐서 간의 연산을 해보자.

    # 텐서플로우 불러오기
    import tensorflow as tf
    
    # 스칼라 정의해보기
    a = tf.constant(1)
    b = tf.constant(2)
    
    #덧셈
    c = tf.math.add(a, b)
    print(c)
    #뺄셈
    print(tf.math.subtract(a,b))
    #곱셈
    print(tf.math.multiply(a, b))
    #나눗셈
    print(tf.math.divide(a, b))
    #나머지
    print(tf.math.mod(a, b))
    #몫
    print(tf.math.floordiv(a, b))
    ---------------------------------------
    tf.Tensor(3, shape=(), dtype=int32)
    
    tf.Tensor(-1, shape=(), dtype=int32)
    
    tf.Tensor(2, shape=(), dtype=int32)
    
    tf.Tensor(0.5, shape=(), dtype=float64)
    
    tf.Tensor(1, shape=(), dtype=int32)
    
    tf.Tensor(0, shape=(), dtype=int32)

    벡터(Vector)

    벡터는 여러 개의 스칼라 값을 원소로 갖는 1차원 배열로 표현된다. 스칼라 여러 개가 동일한 축 방향으로 나열된다. 

    벡터는 원소로 구성되는 여러 개의 값들이 모여서 하나의 대표성을 갖는 값이 된다.
    쉽게 말해 벡터는 좌표계 공간으로 표현하면 어떤 방향이나 크기를 갖는데 각 원소 값의 크기뿐만 아니라, 원소들이 나열되는 순서도 의미가 있다는 뜻이다.

    # 텐서플로우 및 넘파이 불러오기
    import tensorflow as tf
    import numpy as np
    
    # 1차원 배열 정의
    python_list = [10., 20., 30.] # 파이썬 리스트 사용
    numpy_arr = np.array([10., 11., 12., 13.]) # 넘파이 배열 사용
    
    # 텐서로 변환
    vector1 = tf.constant(python_list, dtype=tf.float32)
    vector2 = tf.constant(numpy_arr, dtype=tf.float32)
    
    # 텐서 출력
    print(vector1)
    print(vector2)
    -------------------------------------------------------
    tf.Tensor([10. 20. 30.], shape=(3,), dtype=float32)
    tf.Tensor([10. 11. 12. 13.], shape=(4,), dtype=float32)

    constant 함수에 1차원 배열을 입력하면 1차원인 텐서인 벡터로 변환이 된다. 이때, 함수의 입력값으로 파이썬 리스트와 넘파이 배열을 모두 사용이 가능하다. 벡터의 shape 속성은 (원소 개수,) 형태로 표시된다.

    # 랭크 확인
    print(tf.rank(vector1))
    print(tf.rank(vector2))
    ------------------------------------
    tf.Tensor(1, shape=(), dtype=int32)
    tf.Tensor(1, shape=(), dtype=int32)

    랭크는 당연히 랭크가 0인 스칼라 텐서가 아니고 랭크가 1인 벡터이다.

    벡터 연산

    벡터도 연산이 가능하다.

    # 텐서플로우 및 넘파이 불러오기
    import tensorflow as tf
    import numpy as np
    
    # 1차원 배열 정의
    python_list = [10., 20., 30.] # 파이썬 리스트 사용
    numpy_arr = np.array([10., 11., 12.]) # 넘파이 배열 사용
    
    # 텐서로 변환
    vector1 = tf.constant(python_list, dtype=tf.float32)
    vector2 = tf.constant(numpy_arr, dtype=tf.float32)
    
    
    # 덧셈 함수
    add_vec1_vec2 = tf.math.add(vector1, vector2)
    # 덧셈 연산자
    add1_vec1_vec2 = vector1 + vector2
    
    print("결과:", add_vec1_vec2)
    print("랭크:", tf.rank(add_vec1_vec2))
    
    print("결과:", add1_vec1_vec2)
    print("랭크:", tf.rank(add1_vec1_vec2))
    --------------------------------------------------------
    결과: tf.Tensor([20. 31. 42.], shape=(3,), dtype=float32)
    랭크: tf.Tensor(1, shape=(), dtype=int32)
    
    결과: tf.Tensor([20. 31. 42.], shape=(3,), dtype=float32)
    랭크: tf.Tensor(1, shape=(), dtype=int32)

    결과를 보면 20, 31, 42가 나오는데 배열에서 같은 인덱스끼리 더한다고 보면 된다.

    # tf.math 모듈 함수들
    print(tf.math.subtract(vector1, vector2))  # 뺄셈
    print(tf.math.multiply(vector1, vector2))  # 곱셈
    print(tf.math.divide(vector1, vector2))    # 나눗셈
    print(tf.math.mod(vector1, vector2))       # 나머지
    #  두 텐서의 나눗셈을 수행하고, 나눗셈의 결과는 소수점 이하를 버리고 내림한 값
    print(tf.math.floordiv(vector1, vector2))  
    ------------------------------------------------------------------
    tf.Tensor([ 0.  9. 18.], shape=(3,), dtype=float32)
    tf.Tensor([100. 220. 360.], shape=(3,), dtype=float32)
    tf.Tensor([1.        1.8181819 2.5      ], shape=(3,), dtype=float32)
    tf.Tensor([0. 9. 6.], shape=(3,), dtype=float32)
    tf.Tensor([1. 1. 2.], shape=(3,), dtype=float32)
    # 파이썬 연산자
    print(vector1 - vector2)
    print(vector1 * vector2)
    print(vector1 / vector2)
    print(vector1 % vector2)
    print(vector1 // vector2)
    --------------------------------------------------------------------
    tf.Tensor([ 0.  9. 18.], shape=(3,), dtype=float32)
    tf.Tensor([100. 220. 360.], shape=(3,), dtype=float32)
    tf.Tensor([1.        1.8181819 2.5      ], shape=(3,), dtype=float32)
    tf.Tensor([0. 9. 6.], shape=(3,), dtype=float32)
    tf.Tensor([1. 1. 2.], shape=(3,), dtype=float32)
    # 텐서플로우 및 넘파이 불러오기
    import tensorflow as tf
    import numpy as np
    
    # 1차원 배열 정의
    python_list = [10., 20., 30.] # 파이썬 리스트 사용
    numpy_arr = np.array([10., 11., 12.]) # 넘파이 배열 사용
    
    # 텐서로 변환
    vector1 = tf.constant(python_list, dtype=tf.float32)
    vector2 = tf.constant(numpy_arr, dtype=tf.float32)
    
    
    # 합계 구하기
    print(tf.reduce_sum(vector1))
    print(tf.reduce_sum(vector2))
    ------------------------------------------------------
    tf.Tensor(60.0, shape=(), dtype=float32)
    tf.Tensor(33.0, shape=(), dtype=float32)
    # 합계 구하기
    print(tf.math.square(vector1)) # 거듭제곱
    print(vector1 ** 2)            # 파이썬 거듭제곱
    
    print(tf.math.sqrt(vector1)) # 제곱근
    print(vector1 ** 0.5)        # 파이썬 제곱근
    
    # 텐서의 경우 브로드캐스팅 연산을 지원
    print(vector1 + 1)
    --------------------------------------------------------------------
    tf.Tensor([100. 400. 900.], shape=(3,), dtype=float32)
    tf.Tensor([100. 400. 900.], shape=(3,), dtype=float32)
    tf.Tensor([3.1622777 4.472136  5.477226 ], shape=(3,), dtype=float32)
    tf.Tensor([3.1622777 4.472136  5.477226 ], shape=(3,), dtype=float32)
    tf.Tensor([11. 21. 31.], shape=(3,), dtype=float32)

    행렬(Matrix)

    행렬은 차수가 1인 벡터를 같은 축 방향으로 나열하는 개념이다. 즉, 여러 개의 1차원 벡터를 원소로 갖는 1차원 배열이다. 이해가 안 된다면 맨 위에 사진을 다시 보자. 원소의 차수가 1이므로 총차수는 2가 된다. 따라서 텐서플로우에서는 랭크-2 텐서라고 부른다.

    '랭크-2' 텐서는 여러 개의 1차원 배열(랭크-1)을 원소로 갖는 벡터 형태로 표현이 되기 때문에 행(row)과 열(column)이라는 2개의 축을 갖는 2차원 구조로 표현한다.

    아 참고로 mat이 무슨 의미로 변수를 선언한 거냐 할 수 있는데 matrix에서 앞에 3글자 잘라온 거다.

    # 2차원 배열 정의
    two_dimensional_array =[[10, 20], [30, 40]]
    
    # 텐서로 변환 -> constant 함수에 입력
    mat = tf.constant(two_dimensional_array)
    
    print("결과:", mat)
    print("랭크:", tf.rank(mat))
    ------------------------------------------------
    결과: tf.Tensor(
    [[10 20]
     [30 40]], shape=(2, 2), dtype=int32)
    랭크: tf.Tensor(2, shape=(), dtype=int32)

    이렇게 랭크 함수를 이용하여 확인하여 보면 텐서의 차수가 2이다라고 나오며 랭크가 2인 것을 알 수 있다.

    행 방향과 열 방향으로 2개의 축이 존재하기 때문이다.

    stack 함수

    # 1차원 벡터 정의
    vec1 = tf.constant([10, 20])
    vec2 = tf.constant([30, 40])
    
    # 텐서로 변환 -> stack 함수에 입력하여 위 아래로 쌓기
    mat = tf.stack([vec1, vec2])
    
    print("결과:", mat)
    print("랭크:", tf.rank(mat))
    -------------------------------------------
    결과: tf.Tensor(
    [[10 20]
     [30 40]], shape=(2, 2), dtype=int32)
    랭크: tf.Tensor(2, shape=(), dtype=int32)

     

    행렬 연산

    2차원 구조인 행렬도 벡터와 같이 math 모듈을 이용해서 수학적인 연산이 가능하다.

    벡터와 마찬가지로 같은 위치에 있는 원소들끼리(element-by-element) 짝을 이루어 계산한다.

    # 텐서플로우 및 넘파이 불러오기
    import tensorflow as tf
    
    list1 = [[10, 20], [30, 40]]
    list2 = [[1, 0], [-1, 2]]
    
    # 2차원 배열 정의
    mat1 = tf.constant(list1)
    mat2 = tf.constant(list2)
    
    print("덧셈 결과:", tf.math.add(mat1, mat2))
    print("뺄셈 결과:", tf.math.subtract(mat1, mat2))
    print("곱셈 결과:", tf.math.multiply(mat1, mat2))
    
    # 브로드캐스팅도 가능!
    print("브로드캐스팅 결과:", tf.math.multiply(mat1, 3))
    
    # 행렬곱 연산
    print("행렬곱 연산 :", tf.matmul(mat1, mat2))
    -----------------------------------------------
    덧셈 결과: tf.Tensor(
    [[11 20]
     [29 42]], shape=(2, 2), dtype=int32)
    뺄셈 결과: tf.Tensor(
    [[ 9 20]
     [31 38]], shape=(2, 2), dtype=int32)
    곱셈 결과: tf.Tensor(
    [[ 10   0]
     [-30  80]], shape=(2, 2), dtype=int32)
    브로드캐스팅 결과: tf.Tensor(
    [[ 30  60]
     [ 90 120]], shape=(2, 2), dtype=int32)
    행렬곱 연산 : tf.Tensor(
    [[-10  40]
     [-10  80]], shape=(2, 2), dtype=int32)

    고차원 텐서(Tensor)

    이번에는 축이 3개 이상인 고차원 텐서에 대해 보는데 랭크-3인 텐서는 쉽게 말해 행렬이 여러 개가 모여 고차원 텐서가 되는 것이다.

    랭크-1 텐서를 같은 축 방향으로 결합하면 랭크-2 텐서가 되고 랭크-2 텐서를 동일한 축 방향으로 결합하면 랭크-3 텐서가 된다.

    즉, 1차원 벡터를 나열하면 2차원 행렬이 되고 2차원 행렬을 나열하면 3차원 텐서가 된다.

    # 텐서플로우 및 넘파이 불러오기
    import tensorflow as tf
    import numpy as np
    
    # 2차원 배열 정의
    mat1 = [[1, 2, 3, 4],
            [5, 6, 7, 8]]
    
    mat2 = [[9, 10, 11, 12],
            [13, 14, 15, 16]]
    
    mat3 = [[17, 18, 19, 20],
            [21, 22, 23, 24]]
    
    # 텐서로 변환 -> constant 함수에 입력
    tensor = tf.constant([mat1, mat2, mat3])
    
    print("결과:", tensor)
    print("랭크:", tf.rank(tensor))
    --------------------------------------------------
    결과: tf.Tensor(
    [[[ 1  2  3  4]
      [ 5  6  7  8]]
     [[ 9 10 11 12]
      [13 14 15 16]]
     [[17 18 19 20]
      [21 22 23 24]]], shape=(3, 2, 4), dtype=int32)
    랭크: tf.Tensor(3, shape=(), dtype=int32)

    마찬가지로 stack 함수를 사용하여서 3개의 벡터를 결합할 수 있다.

    # 텐서로 변환 -> constant 함수에 입력
    tensor = tf.stack([mat1, mat2, mat3])
    
    print("결과:", tensor)
    print("랭크:", tf.rank(tensor))
    -------------------------------------------
    결과: tf.Tensor(
    [[[ 1  2  3  4]
      [ 5  6  7  8]]
     [[ 9 10 11 12]
      [13 14 15 16]]
     [[17 18 19 20]
      [21 22 23 24]]], shape=(3, 2, 4), dtype=int32)
    랭크: tf.Tensor(3, shape=(), dtype=int32)