데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      MLX: Apple silicon 용 Machine Learning 프레임워크 - 02.Regression example

      감자 24.01.02
      1,622 13 0
      DEVOTEE 요약
      이 블로그는 MLX 라이브러리를 사용한 기계학습에 대해 설명하고 있다. 선형회귀와 로지스틱회귀를 통한 학습 예시를 제공하며, MLX와 numpy의 성능을 비교한다. 이를 통해 사용자가 MLX를 사용하는 방법을 이해하고, 성능 차이를 확인하는데 도움을 준다.
      DEVOTEE 추천 블로그

      MLX examples

      몇 가지 Machine learning 예제들을 통해 MLX 사용법을 익혀보도록 하겠습니다.

      사실 numpy 나 torch, scipy 등의 라이브러리에 익숙하다면 그렇게 크게 어렵지 않기 때문에 잘 따라할 수 있습니다. 메쏘드들이 거의 유사하게 구현되었거든요.


      Linear Regression

      아주 간단한 Linear Regression 예제를 돌려보겠습니다.

      임의의 함수를 만들어 데이터를 합성하고, 해당 데이터를 이용해 역으로 함수를 approximate 하는 예제입니다.


      우선 관련 모듈들을 import 하고 hyperparam 들을 세팅해줍니다.

      import mlx.core as mx
      import time
      
      num_features = 100
      num_examples = 1_000
      test_examples = 100
      num_iters = 10_000  # iterations of SGD
      lr = 0.01  # learning rate for SGD

      임의의 선형 함수를 만들어주고, 임의의 input 데이터를 만들어줍니다.

      모두 mx.random.normal 을 이용해 랜덤하게 만들어 주겠습니다.label 값의 경우 만들어진 input 데이터를 함수에 통과시키고, 작은 noise 를 부여하여 만들어줍니다.

      # 임의의 선형 함수 True parameters
      w_star = mx.random.normal((num_features,))
      
      # Input examples (design matrix)
      X = mx.random.normal((num_examples, num_features))
      
      # Noisy labels
      eps = 1e-2 * mx.random.normal((num_examples,))
      y = X @ w_star + eps

      별도의 테스트셋도 만들어주겠습니다.

      # Test set generation
      X_test = mx.random.normal((test_examples, num_features))
      y_test = X_test @ w_star

      이제 Loss function 과 Gradient function 을 만들어줍니다. 우선은 MSE loss 를 사용합니다.$${L} _{MSE}=\frac{1}{2n}\sum{(y-pred)^2}$$

      Gradient function 의 경우 수식을 구현해서 사용해도 좋지만, mx.grad 를 이용하면 loss function 으로부터 바로 얻어낼 수 있습니다.

      $$\ nabla{L}_{MSE}=\frac{1}{n}X^T(Xw-y)$$

      # MSE Loss function
      def loss_fn(w):
          return 0.5 * mx.mean(mx.square(X @ w - y))
      
      # Gradient function
      grad_fn = mx.grad(loss_fn)
      
      # 실제 수학적 구현은 아래와 같다. 수행시간도 동일함
      # def grad_fn(w):
      #     return X.T @ (X @ w-y) * (1/num_examples)

      이제 Linear regression 을 위한 parameter 를 초기화하고 SGD (Stochastic Gradient Descent) 방법을 이용해 학습합니다.

      Test set 에 대해서 MSE 값이 크게 감소한 것을 확인할 수 있습니다.

      또 수행시간은 약 0.9초 남짓하게 걸리네요. (M1 맥북 pro 기준입니다)

      Throughput 은 약 10K iter/s 입니다.

      # Initialize random parameter
      w = 1e-2 * mx.random.normal((num_features,))
      
      # Test error (MSE)
      pred_test = X_test @ w
      test_error = mx.mean(mx.square(y_test - pred_test))
      print(f"Initial test error (MSE): {test_error.item():.5f}")
      Initial test error (MSE): 90.18447
      # Training by SGD
      start = time.time()
      for its in range(1,num_iters+1):
          grad = grad_fn(w)
          w = w - lr * grad
      mx.eval(w)
      end = time.time()
      print(f"Training elapsed time: {end-start} seconds")
      print(f"Throughput {num_iters/(end-start):.3f} it/s")
      Training elapsed time: 0.9663820266723633 seconds
      Throughput 10347.875 it/s
      # Test error (MSE)
      pred_test = X_test @ w
      test_error = mx.mean(mx.square(y_test - pred_test))
      print(f"Final test error (MSE): {test_error.item():.5f}")
      Final test error (MSE): 0.00001


      CPU 연산과의 수행시간 비교

      만약 numpy array 를 사용했다면 그 속도는 어떻게 될까요?

      동일한 구현 방식으로 연산 수행 속도를 비교해 보겠습니다.

      물론 numpy 모듈은 Apple silicon 의 GPU 를 사용할 수 없습니다.

      # True parameters
      w_star = np.random.normal(size=(num_features,1))
      
      # Input examples (design matrix)
      X = np.random.normal(size=(num_examples, num_features))
      
      # Noisy labels
      eps = 1e-2 * np.random.normal(size=(num_examples,1))
      y = np.matmul(X, w_star) + eps
      
      # Test set generation
      X_test = np.random.normal(size=(test_examples, num_features))
      y_test = np.matmul(X_test, w_star)
      def loss_fn(w):
          return 0.5 * np.mean(np.square(np.matmul(X, w) - y))
      
      def grad_fn(w):
          return np.matmul(X.T, np.matmul(X, w)-y) * (1/num_examples)
      w = 1e-2 * np.random.normal(size=(num_features,1))
      
      pred_test = np.matmul(X_test, w)
      test_error = np.mean(np.square(y_test - pred_test))
      print(f"Initial test error (MSE): {test_error.item():.5f}")
      Initial test error (MSE): 51.48214
      start = time.time()
      for its in range(1,num_iters+1):
          grad = grad_fn(w)
          w = w - lr * grad
          
      end = time.time()
      print(f"Training elapsed time: {end-start} seconds")
      print(f"Throughput {num_iters/(end-start):.3f} it/s")
      Training elapsed time: 1.2018659114837646 seconds
      Throughput 8320.396 it/s
      pred_test = np.matmul(X_test, w)
      test_error = np.mean(np.square(y_test - pred_test))
      print(f"Final test error (MSE): {test_error.item():.5f}")
      Final test error (MSE): 0.00001

      수행시간이 약 1.2초로 증가한 것을 확인할 수 있습니다.

      Throughput은 8.3K 정도로 약간 감소했네요.

      feature 도 100개 정도인 아주 간단한 linear regression 에서도 이 정도의 속도 증가는 기대할 수 있겠습니다.

      드라마틱한 변화는 발생하지 않는 걸 볼 수 있습니다.


      Logistic regression

      Logistic regression 도 잠깐 테스트를 해보도록 하겠습니다.

      모든 과정은 Linear regression 과 동일하지만 Label 이 0,1 로 구성된다는 것, activation function 을 사용한다는 것, Loss 로 Cross entropy 를 사용한다는 것만 다릅니다.

      import time
      
      import mlx.core as mx
      
      num_features = 100
      num_examples = 1_000
      num_iters = 10_000
      lr = 0.01
      
      # True parameters
      w_star = mx.random.normal((num_features,1))
      
      # Input examples
      X = mx.random.normal((num_examples, num_features))
      
      # Labels (with noise)
      eps = 1e-2 * mx.random.normal((num_examples,1))
      y_bool = (X @ w_star + eps) > 0
      y = mx.where(y_bool, 1.0, 0.0)
      
      # Initialize random parameters
      w = 1e-2 * mx.random.normal((num_features,1))
      
      def loss_fn(w):
          logits = X @ w
          y_hat = mx.sigmoid(logits)
          return mx.mean(-y * mx.log(y_hat) - (1-y) * mx.log(1-y_hat))
      
      # def grad_fn(w):
      #     logits = X @ w
      #     y_hat = mx.sigmoid(logits)
      #     return X.T @ (y_hat - y) * (1/num_examples)
          
      grad_fn = mx.grad(loss_fn)
      
      tic = time.time()
      for iters in range(num_iters):
          grad = grad_fn(w)
          w = w - lr * grad
      
          # Early stopping
          preds = (X @ w) > 0
          acc = mx.mean(preds == y_bool)
          if acc > 0.95:
              print(f"Early stop at iteration {iters}")
              break
      
      mx.eval(w)
      toc = time.time()
      
      loss = loss_fn(w)
      final_preds = (X @ w) > 0
      acc = mx.mean(final_preds == y)
      
      throughput = iters / (toc - tic)
      print(
          f"Loss {loss.item():.5f}, Accuracy {acc.item():.5f} "
          f"Throughput {throughput:.5f} (it/s)"
      )
      Early stop at iteration 256
      Loss 0.44712, Accuracy 0.95000 Throughput 1627.90763 (it/s)
      import time
      
      import numpy as np
      
      num_features = 100
      num_examples = 1_000
      num_iters = 10_000
      lr = 0.01
      
      # True parameters
      w_star = np.random.normal(size=(num_features,))
      
      # Input examples
      X = np.random.normal(size=(num_examples, num_features))
      
      # Labels
      eps = 1e-2 * np.random.normal(size=(num_examples,))
      y_bool = np.matmul(X, w_star) + eps > 0
      y = np.where(y_bool, 1.0, 0.0)
      
      
      # Initialize random parameters
      w = 1e-2 * np.random.normal(size=(num_features,))
      
      def sigmoid(x):
          return 1 / (1 + np.exp(-x))
      
      def loss_fn(w):
          logits = np.matmul(X, w)
          y_hat = sigmoid(logits)
          return np.mean(-y * np.log(y_hat) - (1-y) * np.log(1-y_hat))
      
      def grad_fn(w):
          logits = np.matmul(X, w)
          y_hat = sigmoid(logits)
          return np.matmul(X.T, y_hat - y) * (1/num_examples)
      
      tic = time.time()
      for iters in range(num_iters):
          grad = grad_fn(w)
          w = w - lr * grad
      
          # Early stopping
          preds = np.matmul(X, w) > 0
          acc = np.mean(preds == y_bool)
          if acc > 0.95:
              print(f"Early stop at iteration {iters}")
              break
      
      toc = time.time()
      
      loss = loss_fn(w)
      final_preds = np.matmul(X, w) > 0
      acc = np.mean(final_preds == y)
      
      throughput = iters / (toc - tic)
      print(
          f"Loss {loss.item():.5f}, Accuracy {acc.item():.5f} "
          f"Throughput {throughput:.5f} (it/s)"
      )
      Early stop at iteration 297
      Loss 0.40303, Accuracy 0.95100 Throughput 3858.49777 (it/s)

      재미있게도 numpy 를 사용하는 것이 throughput 이 더 이득이네요.

      추후 multi-layer perceptron 처럼 행렬 연산이 더 무거워지는 경우 어떻게 되는지 확인해보도록 하겠습니다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      감자 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기