2025년 10월 29일 수요일

2025-10-29 현시점에서 개인 개발자를 위한 가장 현실적인 인공지능 학습을 위한 GPU workstation

 GPU - Nvidia RTX 4090(24GB) : 학습데이터 메인메모리에서 GPU로 옮기다가 에러나가 싫으면 24GB는 되어야 한다고 생각한다.

CPU - 사실 별로 중요하지 않다. 이왕이면 AMD로

RAM - 뭐가 되었든 최소 64GB 이상 데이터를 그래프화 하거나 저장하거나 할때 말도 않되는 메모리 사용량에 프로그램이 멈추는 경험을 하기 싫다면 많을 수록 좋다.

메인보드 - 당장 GPU를 하나밖에 달 수 없는 주머니 사정을 어쩔 수 없더라도 누가 알겠는가 어쩌다 돈이 떨어져서 GPU를 하나 더 달 수 있는 상황이 발생할지도 그러니까 그러니 GPU를 어러게 달 수 있는 메인보드를 선택하자 당장은 어쩔 수 없이 하나만 달아서 쓰더라도.

 파워서플라이 - 850W 이상 끝.

Keras 정확히는 Tensorflow의 메모리 부족 문제 해결책

 처음에는 학습데이터의 양이 메인 메모리의 한계를 넘어가면서 메모리를 증설했고 그 다음에는 학습데이터를 GPU에 옮기는 과정에서 GPU 메모리가 부족하여 batch_size를 줄여도 보았으나 다음과 같은 에러 메시지는 해결되지 않았다. 

" InternalError: Failed copying input tensor from /job:localhost/replica:0/task:0/device:CPU:0 to /job:localhost/replica:0/task:0/device:GPU:0 in order to run _EagerConst: Dst tensor is not initialized."

 확실하진 않지만 위와 같은 메시지 였던것 같다. CPU에서 다루던 데이터를 GPU로 옮기다가 사고가 나는 것 같다. 이치상으로 보면 batch_size를 줄이면 해결되어야 하는데 해결되지 않았고 어디서 다음과 같은 DataGenerator로 조금씩 데이터를 흘려 주면 된다고 했는데 나한테는 전혀 효과가 없었다.


class DataGenerator(Sequence):
  def __init__(self, x_set, y_set, batch_size):
    self.x, self.y = x_set, y_set
    self.batch_size = batch_size

  def __len__(self):
    return int(np.ceil(len(self.x) / float(self.batch_size)))

  def __getitem__(self, idx):
    if idx == self.__len__() - 1:
      batch_x = self.x[idx * self.batch_size : ]
      batch_y = self.y[idx * self.batch_size : ]
    else:
      batch_x = self.x[idx * self.batch_size : (idx+1) * self.batch_size]
      batch_y = self.y[idx * self.batch_size : (idx+1) * self.batch_size]
    return batch_x, batch_y

train_gen = DataGenerator( X_train_split,  y_train_split,  batch_size)
test_gen = DataGenerator(X_val_split, y_val_split, batch_size)

history = self.model.fit(
 train_gen,
 validation_data=test_gen,
 epochs=epochs,
 callbacks=callbacks,
 verbose=1

 정확히 문제가 되는 지점이 학습 완료 후 평가하는 과정에서 사단이 나서 굉장히 열받는 상황이었다. 며칠을 걸려서 학습을 완료했는데 왜 평가할때 프로그램이 사망하냐구.

 아무튼 좀 더 구글링을 해서 찾은 해결책은 학습기간 동안 최적의 모델을 파일형태로 저장하도록 하고 기존 모델을 제거해서 메모리 문제를 제거하는 방법을 시도해 보았다. 그리고 최적의 모델은 다시 파일에서 로딩하는 방식으로... 하지만 다음 명령어는 효과가 없었다.

del model

 뭔가 cuda와 cuDNN 그리고 tensorflow의 backend 사이에 꼬여 있는 부분이 있는 것 같다. 여러 차례의 시도를 하여 각 tensorflow의 backend의 각세션을 학습 완료 후 재 초기화 하고 "del model"을  수행한다. 그리고 garbage collect을 통해 안쓰는 데이터를 메모리에서 해제한다. 그 결과 평가 단계로 수훨하게 넘어 갈 수 있었다. 그 기록을 여기에 남겨 둔다.

 ... 학습 완료 후...

    # reset Keras Session
    sess = tf.compat.v1.keras.backend.get_session()
    tf.compat.v1.keras.backend.clear_session()
    sess.close()
    sess = tf.compat.v1.keras.backend.get_session()
    del self.model  # this is from global space - change this as you need
    # use the same config as you used to create the session
    config = tf.compat.v1.ConfigProto()
    config.gpu_options.per_process_gpu_memory_fraction = 1
    config.gpu_options.visible_device_list = "0"
    tf.compat.v1.keras.backend.set_session(tf.compat.v1.Session(config=config))

    import gc

    gc.collect()

... 저장된 파일에서 self.model을 다시 로드한다...

2025년 7월 17일 목요일

너무나도 넘쳐나는 대 배터리의 시대

 모든 전자기기에 배터리가 기본으로 장착되는 것 같다. 서랍에 짱박아 논 외장 배터리 송풍기 등등 너무나도 넘쳐난다. 하지만 난 한번에 하나밖에 쓸 수 밖에 없다. 맘이 불편하다. 이젠 쓰고 싶지 않아도 필수로 배터리가 장착된 제품만 존재한다.

1. 대 배터리의 시대

2. 배터리없이 쓸 수 있는 제품이 없다.

3. 배터리 없는 것 쓰고 싶다. 

2025년 5월 22일 목요일

PCB Layering for 4 layers and 6 layers

- 4 Layers

Signal + Component

GND

Power

Low speed signal


- 6 Layers

Signal + Component

GND

Power

Signal

GND

Signal


2024년 7월 22일 월요일

freecad의 충돌 그리고 실행을 위한 python3.11의 libshiboken2, libpyside2 링크 만들기

 잘쓰던 freecad가 python3의 버전이 오롯이 3.12로 재편 되면서 기존의 3.11이 있음에도 불구하고 3.11이 제대로 돌아가지 못하게 되었다. 

 그래서 덩달아 freecad도 실행이 불가능한 상태가 되어 버렸다. 해결해 볼라구 찾아 보았다 크게 2개의 문제가 되더라 libpyside2와 libshiboken2, 이 둘이 freecad의 GUI를 담당하는데 찾고 있던 경로(/usr/lib/python3/dist-packages/)에 없었던 듯 하다.

 데비안 패키지 저장소를 뒤졌으나 libshiboken2-py3-5.15_5.15.10-4_amd64.deb 밖에 없었다. 범위를 넓혀서 rpm도 찾아 보았다. 그래서 데비안 libpyside2 패키지를 대체할 수 있는 pyside2-core-5.15.12-2-omv2490.x86_64.rpm를 다운로드 받았다. 

 데비안 현시점(2024-07-22)의 testing 배포판의 python3의 주력이 3.12으로 넘어가 버려서 /usr/lib/python3/dist-packages에 3.11 버전의 패키지를 덮어 씌우는 것은 시스템을 망가트릴 수 있어 아래의 경로에 해당 라이브러리들(libpyside2*, libshiboken2*)을 복사해 두었다.

 $ mv /home/daysleep/Downloads/libpyside2.cpython-311-x86_64-linux-gnu.so.5.15* /usr/lib/x86_64-linux-gnu/
$ mv /home/daysleep/Downloads/libshiboken2/libshiboken2-py3-5.15_5.15.10-4_amd64/usr/lib/x86_64-linux-gnu/libshiboken2.cpython-311-x86_64-linux-gnu.so.5.15* /usr/lib/x86_64-linux-gnu/
$ cp -r /home/daysleep/Downloads/libshiboken2/libshiboken2-py3-5.15_5.15.10-4_amd64/data/usr/lib/python3/dist-packages/shiboken2 /usr/local/lib/python3.11/dist-packages/

뭐 그래도 여전히 유독 /usr/lib/python3/dist-packages에서만 찾는 plugin 자원들은 문제가 생기더라 뭐 찝찝하지만 실행은 되니 우선 임시적으로 이렇게 써야 겠다.

* 주의 사항: symbolic link 생성시 반드시 작업 디레토리를 생성 경로로 설정해야 생성이 되더라 한참 애먹었다. 

$ ln -s libpyside2.cpython-311-x86_64-linux-gnu.so.5.15.12 libpyside2.cpython-311-x86_64-linux-gnu.so.5.15

2024년 3월 17일 일요일

Python의 pandas를 사용한 데이터 처리 코드 해석

 Python의 코딩 스타일이라는 것이 정해져 있진 않지만 C/C++ 기반으로 시작하여 여기까지 와버린 나로써는 JavaScript 언어의 매써드 붙여쓰기 / for 문 대신 apply() 메써드 붙여쓰기는 당황 스럽다. 어쩌겠나 적응해야지. 익숙해 질때까진 새것 발건한 것 마냥 기록 좀 남겨야 겠다.

md['genres'] = md['genres'].fillna('[]').apply(literal_eval).apply(
    lambda x: [i['name'] for i in x] if isinstance(x, list) else []

 위의 코드는 pandas의 dataframe인 md를 조작하는 코드다; 결과적으로 'genres' 항목의 내용이 dict를 내부항목으로 하는 list로 되어 있는 것을 해체해서 'name'항목으로만 되어 있는 list로 만들어 버린다. 이를 위해서 코드가 좀 돌다리를 두드리면서 실행하도록 만들어 두었다. 안전한게 좋긴하지만 코드 보기가 눈아프긴 하다.

 우선, fillna('[]')는 md['genres']인 Series의 내부항목중 Na 혹은 NaN 즉, 숫자가 아니거나 아무것도 없거나 한 곳은 '[]' 문자열(실제 내용은 빈 list)을 넣어 두고,

 그 다음에 dict를 내부항목으로 갖는 list를 가지고 작업하려고 하는데 아쉽게도 해당 내용이 Series의 문자열 형태로 되어 있어서 이 부분을 해석해서 Python의 객체로 만들어 주어야 한다. 이 작업을 해주는 것이 .apply(literal_eval)이라는 것이다.

 그리고 나서 본격적으로  Series의 내부항목 list의 내용을 구조가 복잡한 dict를 해체해서 'name'항목만 남겨 주는 혹은 내용이 없으면 빈 list로 재편한다. 이 작업을 .apply()메서드와 dict의 내용을 해체하고 빈곳은 빈 list로 대체하는 lambda 함수가 해주고 있다. 

vote_counts = md[md['vote_count'].notnull()]['vote_count'].astype('int')

 이것도 마찬가지다. dataframe인 md의 'vote_count' 컬럼의 내용중 내용이 있는 항목만 추려 혹시나 있을 누락된 항목으로 발생할 수 있는 에러를 방지 하도록 해놓았다. 

 그래서, 유효한(notnull)내용만 갖는 'vote_count' 컬럼의 내용을 마지막으로 정수형으로 변환하여 vote_count에 저장하도록 해놓았다.



2023년 9월 10일 일요일

C++ value 용어 해설(lvalue, glvalue, xvalue, rvalue, prvalue)

원문: - New Value Terminology - Bjarne Stroustrup [https://www.stroustrup.com/terminology.pdf])

# C++ value terminology 

## lvalue

- 역사적으로 lvalue은 대입 표현식(A = B)의 왼쪽에 위치하곤 하기 때문에 left-value의 의미 불립니다. lvalue는 객체나 함수를 지칭하도록 의도되었습니다. 예를 들면, E가 포인터 형태의 표현식일때, *E는 E가 가르키는 객체나 함수를 가르키는 lvalue 표현식입니다. 다른 예로, 반환값의 타입이 lvalue인 함수를 호출한 결과는 lvalue 입니다.

## xvalue

- eXpiring value(만료값)로써, 일반적으로 수명이 거의 끝나가는 객체를 참조하기도 하는데, 예를 들어 리소스를 이동할 수 있도록 수명이 다한 객체를 참조하기도 합니다. xvalue는 rvalue 참조와 관련된 특정 종류의 표현식의 결과입니다. 예를 들면, 반환 유형이 rvalue 참조인 함수를 호출한 결과는 xvalue입니다.

## glvalue

- generalized lvalue(일반 lvalue)로써, lvalue 또는 xvalue를 의미합니다.

## rvalue

- 역사적으로 대입표현식의 오른쪽에 나타날 수 있기 때문에 right value의 의미로 불립니다. rvalue는 xvalue이기도 하고, 임시 객체 혹은 그 하위 객체, 객체와 연관되지 않는 값을 의미합니다.

## prvalue

- pure rvalue(순수 rvalue)로써, xvalue가 아닌 rvalue를 의미합니다. 예를 들면, 반환 유형이 참조가 아닌 함수를 호출한 결과는 prvalue입니다.

## 모델

- i : 구분성이 있는 - 즉, 주소, 포인터, 사용자가 두 사본이 동일한지 여부를 확인할 수 있습니다.

- m : 이동되어질 수 있는 - 즉, 정해지지 않았지만 유효한 상태에서 "복사"의 원본으로 보내는 것이 허용됩니다.

- iM : 구분성이 있으며, 이동되어질 수 없습니다.

- im : 구분성이 있으며, 이동되어질 수 있습니다.(예: lvalue를 rvalue 참조로 타입변환한 결과)

- Im : 구분성이 없으며, 이동되어질 수 있습니다.

iM   im   Im
    \ /    \ /
      i    m

왼쪽의 두 점("iM"과 "i"로 표시된)은 형식을 구분하는 사람들이 "lvalue"이라고 부르는 것이고, 오른쪽의 두 점("m"과 "Im"으로 표시된)은 형식을 구분하는 사람들이 "rvalue"이라고 부르는 것입니다.
즉, W의 왼쪽 "다리"는 "lvalue"와 관련된 이름을 가져야 하고, W의 오른쪽 "다리"는 "rvalue"와 관련된 이름을 가져야 합니다.

저는 표준 라이브러리 문구에서 "rvalue"가 "m"(일반화)을 의미한다는 사실을 발견했고, 표준 라이브러리의 기대와 텍스트를 보존하려면 W의 오른쪽 하단 지점을 "rvalue"로 명명해야 한다고 생각했습니다.

lvalue       xvalue      prvalue
         \        /     \         /
     iM \      /  im  \      / Im
           \    /          \    /
            \  / i          \  / m
             \/              \/
       glvalue        rvalue

W의 왼쪽 위 점은 "lvalue"이고 오른쪽 아래 점은 "rvalue"입니다. 왼쪽 하단 점은 이동이 가능한 고전적인 lvalue의 일반화입니다. 따라서 "generalized lvalue"입니다. 우리는 이것을 "glvalue"라고 명명했습니다. W의 오른쪽 위 점은 오른쪽 아래 점보다 덜 일반적입니다(지금은 예전과 마찬가지로 "rvalue"라고 부릅니다). 이 지점은 (소멸자를 제외하고는) 다시 참조할 수 없기 때문에 이동할 수 있는 객체의 원래 순수한 개념을 나타냅니다. 저는 "generalized lvalue"와 대조되는 "specialized rvalue"라는 표현이 마음에 들었지만 "prvalue"로 축약된 "pure rvalue"를 사용하였습니다.(그리고 아마도 당연한 결과일 것입니다). 따라서 W의 왼쪽 다리는 "lvalue"와 "glvalue"이고 오른쪽 다리는 "prvalue"와 "rvalue"입니다. 덧붙여서, 모든 값은 glvalue 또는 prvalue 중 하나이지만 둘 다는 아닙니다.