2025년 2월 13일 목요일

FSM (유한상태기계) 사용 해보기

transitions라는 pypi 라이브러리가 있어서 사용해 보았음. 계층 구조의 transition도 표현해주고 기능이 풍부함.

작성된 상태도를 그려보기 위해서는 pygraphviz설치가 필요한데, 아래 절차대로 설치한다:


windows환경에 graphviz와 pygraphviz 설치

1. 가상환경(py310)에 들어간다

2. graphviz 툴을 설치한다: https://pygraphviz.github.io/documentation/stable/install.html에 들어가서 다운 받아 설치하거나 또는 https://graphviz.org/download/#windows에가서 다운 받아 설치한다. 

설치할 때, 경로를 "사용자 모두" 또는 "현재 사용자"로 선택

3. graphviz를 설치했으면 pip install graphviz하면 설치됨

4. pygraphviz 설치를 위해서는 visual studio make 도구 설치가 필요함: https://visualstudio.microsoft.com/ko/visual-cpp-build-tools/에 들어가서 Build Tools를 다운로드 하고 설치한다. 7Gb 메모리 필요

5. 설치가 완료되면 재부팅을 한다

6. pygraphviz 설치: https://pygraphviz.github.io/documentation/stable/install.html에 설명된 명령을 참고하여, 

>> pip install --config-settings="--global-option=build_ext" --config-settings="--global-option=-IC:\Program Files\Graphviz\include" --config-settings="--global-option=-LC:\Program Files\Graphviz\lib" pygraphviz

이렇게 하여 설치를 완료 하였음



2025년 1월 7일 화요일

L2 regularization

 DNN학습할 때, 학습 샘플 수가 작을 경우가 많다. 이 때, 모델은 데이터에 과적합된다. 과적합을 피하기 위해 data augmentation이나, dropout등을 적용하면 학습 자체가 잘 되지 않는 일이 발생한다. 

이럴 때 해볼 수 있는 옵션 중에 L2 regularization이 있다. 

skorch기반으로 다변량 시계열 데이터에 대해 regression학습을 해보면 가중치의 제곱합(Weights Sum)값이 아래와 같다. 


valid_loss는 계속 줄고 있지만, 가중치합이 급격히 증가하고 있다. 즉, 모델이 과적합 되고 있다. 

이번에는 Loss항에 L2 regularization항을 포함시키고, 다시 학습을 시키면 다음과 같다.


W Sum항을 살펴보면 값이 줄고 있고, 범위 내에서 관리되고 있다. 즉, L2 Loss항과 MSE Loss항이 합쳐져서 train_loss값이 된다. 

이 때 L2 Loss의 가중치인 Ramda값의 크기를 잘 결정해 주어야 한다. 

- L2 regularization 적용 시, Weights Sum이 작아짐. 

- L2 loss의 크기가 최적화되었을 때의 train_loss나 valid_loss의 절반 이하가 되게 Weight_decay값을 선정

- 즉, L2 Loss가 원래 Loss의 감소에 영향을 주지 않을 정도로 Ramda값을 정해 준다.





2023년 12월 1일 금요일

timescaledb 사용하기

 1. docker compose 파일 작성

version: "3.8"

services:
  timescaledb:
    image: timescale/timescaledb:latest-pg14
    container_name: timescale
    hostname: timescaledb
    restart: always
    ports:
      - ${TIMESCALEDB_PORT}:5432
    volumes:
      - ./${TIMESCALEDB_DATA_STORE}:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: ${TIMESCALEDB_PASSWORD}
      POSTGRES_USER: ${TIMESCALEDB_USER}
      POSTGRES_DB: ${TIMESCALEDB_DB}
  adminer:
    image: adminer:4.8.1
    container_name: adminer
    restart: always
    ports:
      - ${ADMINER_PORT}:8080

2. 동일 폴더에 .env파일 작성

# timescaledb
TIMESCALEDB_PORT=5432
TIMESCALEDB_DATA_STORE=timescaledb/
TIMESCALEDB_PASSWORD=timescaledb
TIMESCALEDB_USER=timescale
TIMESCALEDB_DB=timescale_database

# adminer
ADMINER_PORT=8087

3. docker compose up 실행

4. python 코드로 접속 여부 확인

import psycopg2
from pgcopy import CopyManager
# Structure of the connection string:
# "postgres://username:password@host:port/dbname"
CONNECTION = "postgres://timescale:timescaledb@localhost:5432/timescale_database"
conn = psycopg2.connect(CONNECTION)
cursor = conn.cursor()

for id in range(1, 4, 1):
     data = (id,)
     # create random data
     simulate_query = """SELECT generate_series(now() - interval '24 hour', now(), interval '5 minute') AS time,
                        %s as sensor_id,
                        random()*100 AS temperature,
                        random() AS cpu
                     """
     cursor.execute(simulate_query, data)
     values = cursor.fetchall()
     # column names of the table you're inserting into
     cols = ['time', 'sensor_id', 'temperature', 'cpu']
     # create copy manager with the target table and insert
     mgr = CopyManager(conn, 'sensor_data', cols)
     mgr.copy(values)

conn.commit()

5. chrome열어서 localhost:8087 접속하여 table생성 여부 체크

6. docker container ls --all 로 container 체크해서 아래 확인

2b2340a3b640   timescale/timescaledb:latest-pg14   "docker-entrypoint.s…"   7 days ago   Up 7 days   0.0.0.0:5432->5432/tcp   timescale
bc41c6ad6171   adminer:4.8.1                       "entrypoint.sh php -…"   7 days ago   Up 7 days   0.0.0.0:8087->8080/tcp   adminer


[참고] 

1. Postres for time series data, Medium

2023년 5월 30일 화요일

yolov8 학습 및 테스트

# yolov8 설치 후 학습과 테스트의 예


# 경로- g:\2023_yolov8
> yolo task=detect mode=train model=yolov8m.pt imgsz=1280 data=fire2023_1.yaml
epochs=50 batch=16 name=yolov8m_v8_50e
> yolo predict model=best.pt source=parking_lot5.mp4  # test

2023년 5월 9일 화요일

pip가 깨졌을 때

 (1) pip가 깨어졌을 때, base에서

conda install --force-reinstall pip


(2) 아래 오류

ERROR: Could not install packages due to an OSError: [Errno 2] No such file or directory: 

> pip3 install --upgrade --user pip

2023년 5월 2일 화요일

python 패턴

 객체 주입 후 한번에 실행하기

from abc import ABC, abstractmethod
from typing import List, Tuple, Union, Dict
import torch


# Base class for creating ts analytic items
# classmethod는 staticmethod와 유사한데, staticmethod는 class변수에 access
# 안 하는데 비해, classmethod는 cls로 access한다.
class ItemBase(ABC):
    @classmethod  # instance 관점이 아닌 class전체 관점에서 변수를 다룰 수 있음
    def add_instance(cls, ins):  # class의 보편적인 값을 다룬다는 의미에서 class
        cls._items.append(ins)  # 약자인 cls를 인자로 받음.
        print(cls._items, len(cls._items))  # self대신 cls전달 받음
       
    @abstractmethod
    def run(self, *args):
        pass
   
   
# Derivated class for outlier detection
# 생성만 하면 객체 리스트가 자동으로 만들어짐
class OutlierDetector(ItemBase):
    _items = []  # ItemBase에 있었다면 모든 자식의 instance를 저장
    def __init__(self, *args):  # 여기에 있으면, 현 class 인스턴스 저장
        print(args)
        self.add_instance(self)
       
    def run(self, *args):
        print(args)
       

# Derivated class for forecasting time series
class Forecastor(ItemBase):
    _items = []
    def __init__(self, *args):
        super().__init__()
        print(args)
        self.add_instance(self)
       
    def run(self, *args):
        print(args)

       
# Collect multiple items and process them at once
class ProcessItems(object):
    def __init__(self, items: List = [])->None:
        self.items = items
       
    def process(self)->List:
        res = []
        for item in self.items:
            res.append(item.run())
        return res
           
       
a3 = ProcessItems([OutlierDetector(), Forecastor()])
res = a3.process()
print(res)


응용 예

from abc import ABC, abstractmethod
from typing import List, Tuple, Union, Dict
import torch
import pdb


# Base class for creating ts(Time sereis) analytic items
class ItemBase(ABC):
    _modes = ['start', 'stable', 'finish']
   
    @abstractmethod
    def run(self, *args):
        pass
   
    # cls로 class변수에 access 가능(staticmethod와 차이점)
    @classmethod
    def _load_weight(cls, items):
        cls.models = {}
        for key, weight in items.items():
            if key not in cls._modes:
                assert False, 'Mode Error!!'
            # cls.models[key] = cls.dnn_model(weight)  
            cls.models[key] = None    
   
# Child class for forecasting time series
class Forecastor(ItemBase):
    def __init__(self, items):
        # self.dnn_model = dnn_model()
        self._load_weight(items)
       
    def run(self, *args):
        batch, mode = args
        # outs = self.models[mode].predict(batch)
        print(batch.shape)
        return batch.shape

# Child class for outlier detection
class AnomalyDetector(ItemBase):
    def __init__(self, items):
        # self.dnn_model = dnn_model()
        self._load_weight(items)
       
    def run(self, *args):
        batch, mode = args
        print(batch.shape)
        return batch.shape

# Main processor to run all items
class Processor(object):
    def __init__(self, items):
        self.items = items
       
    def process(self, ts):
        res = []
        # mode = ts_decision(ts)  # 기동,정상,정지부 판정
        mode = 'start'
        for item in self.items:
            outs = item.run(ts, mode)
            res.append(outs)
        return res


# There are two local processes to handle time series data
forecast = Forecastor({'start':'m1.pt', 'stable': 'm2.pt', 'finish': 'm3.pt'})
anodetec = AnomalyDetector({'start':'m1.pt', 'stable': 'm2.pt', 'finish': 'm3.pt'})
batch = torch.rand(8,32,16)

# If you need to add another process, first define and simply add it to arg list
a1 = Processor([forecast, anodetec])
a1.process(batch)
                torch.Size([8, 32, 16])
                torch.Size([8, 32, 16])
Out[7]:
[torch.Size([8, 32, 16]), torch.Size([8, 32, 16])]

from abc import ABC, abstractmethod
from typing import List, Tuple, Union, Dict
import numpy as np
import torch
import pdb

def _gt(x,v): return True if x >= v else False  # _gt = lambda x, v: True if x >= v else False
def _lt(x,v): return True if x <= v else False  # _lt = lambda x, v: True if x <= v else False
def _eq(x,v): return True if x == v else False  # _eq = lambda x, v: True if x == v else False
def _in(x,v1,v2): return True if (x >= v1) & (x<= v2) else False  # _in = lambda x, v1, v2: True if (x >= v1) & (x<= v2) else False

op1={}
op1['s1'] = _gt
_gt(2,1), _lt(2,1), _in(2,3,4), op1['s1'](2,1)
(True, False, False, True)


# Base class for creating ts(Time sereis) analytic items
class ItemBase(ABC):
    @classmethod
    def add(cls, ins):
        cls._items[ins._name] = ins
       
    @classmethod
    def get_items(cls):
        return list(cls._items.keys())
   
    @classmethod
    def get_conditions(cls):
        items = []
        for k, v in cls._items.items():
            iks = [(ik, str(iv[0]).split()[1]) for ik,iv in v.terms.items()]
            items.append({k: iks})
        return items
   
    def _save_items(self, items):
        if type(items) != dict:
            return None
        for k,v in items.items():
            try:
                self.terms[k] = v
            except Exception as e:
                return None
   
    def refresh(self, ins):
        keys = self.terms.keys()
        satisfied = {}
        for k, v in ins.items():
            if k in keys:
                try:
                    check = self.terms[k][0](*v)
                except:
                    satisfied[k] = -1
                else:
                    self.terms[k][1] = check
                    satisfied[k] = check
        return satisfied    

class Derivated(ItemBase):
    class NoneDict(Dict):
        def __getitem__(self, key):
            return dict.get(self, key)
       
    _items = NoneDict()
   
    def __init__(self, _name, items):
        self.terms = {}
        self._name = _name
        self._save_items(items)


Derivated.add(Derivated('NOx', {'op1':[_gt, False], 'op2': [_lt, False], 'op3': [_in, False]}))
Derivated.add(Derivated('O2', {'op4':[_lt, False], 'op5': [_in, False]}))

if Derivated._items['NOx']:
    print(Derivated._items['NOx'].refresh({'op1': (1,2), 'op2': (3,4), 'op3': (4,1,5)}))
if Derivated._items['O2']:
    print(Derivated._items['O2'].refresh({'op4': (1,2), 'op2': (3,4), 'op3': (4,1,5)}))
{'op1': False, 'op2': True, 'op3': True}
{'op4': True}


Derivated.add(Derivated('NOxIn', {'op1':[_gt, False], 'op2': [_lt, False], 'op3': [_in, False]}))
Derivated.add(Derivated('O2out', {'op4':[_lt, False], 'op5': [_in, False]}))
Derivated.get_items()
['NOx', 'O2', 'NOxIn', 'O2out']


Derivated.get_conditions()
[{'NOx': [('op1', '_gt'), ('op2', '_lt'), ('op3', '_in')]},
 {'O2': [('op4', '_lt'), ('op5', '_in')]},
 {'NOxIn': [('op1', '_gt'), ('op2', '_lt'), ('op3', '_in')]},
 {'O2out': [('op4', '_lt'), ('op5', '_in')]}]



[References]

1. D:\2022\Pattern_Test


 

2023년 3월 26일 일요일

Pytorch forecasting 사용법

위치: D:\2023\TemporalFusionTransformer

설치 방법 (가상환경 torch310) 

pip install pytorch-lightning
pip install pytorch_forecasting
# version < 2.0 for torch gpu version
# 2023년 3월. torch 2.0을 지원하지 않아, gpu버전 사용을 위해 따로 torch 설치
# numpy버전 맞지 않아 재 설치로 오류 해결  
pip install torch==1.13.1+cu117 torchvision==0.14.1+cu117 torchaudio==0.13.1
--extra-index-url https://download.pytorch.org/whl/cu117
pip install numpy<1.24  # Solve version collision


import numpy as np
import pandas as pd
from pytorch_forecasting import Baseline, TemporalFusionTransformer, TimeSeriesDataSet

sample_data = pd.DataFrame(
    dict(
        time_idx=np.tile(np.arange(6), 3),
        target=np.array([0,1,2,3,4,5,20,21,22,23,24,25,40,41,42,43,44,45]),
        group=np.repeat(np.arange(3), 6),
        holidays = np.tile(['X','Black Friday', 'X','Christmas','X', 'X'],3),
    )
)
sample_data

# group이 3개(0, 1, 2), holidays는 각 group에 대해 일정
# time_index는 첫 col에 주어짐.
time_idx    target  group   holidays
0   0   0   0   X
1   1   1   0   Black Friday
2   2   2   0   X
3   3   3   0   Christmas
4   4   4   0   X
5   5   5   0   X
6   0   20  1   X
7   1   21  1   Black Friday
8   2   22  1   X
9   3   23  1   Christmas
10  4   24  1   X
11  5   25  1   X
12  0   40  2   X
13  1   41  2   Black Friday
14  2   42  2   X
15  3   43  2   Christmas
16  4   44  2   X
17  5   45  2   X
#----------------------------------------------------

# create the time-series dataset from the pandas df
dataset = TimeSeriesDataSet(
    sample_data,
    group_ids=["group"],
    target="target",
    time_idx="time_idx",
    max_encoder_length=2,
    max_prediction_length=3,
    time_varying_unknown_reals=["target"],
    static_categoricals=["holidays"],
    target_normalizer=None
)

# pass the dataset to a dataloader
dataloader = dataset.to_dataloader(batch_size=1)

#load the first batch
x, y = next(iter(dataloader))
print(x['encoder_target'])
print(x['groups'])
print(x['decoder_target'])

# 2개의 값을 encoder로 이용. 3개의 값이 prediction
tensor([[21., 22.]])
tensor([[1]])
tensor([[23., 24., 25.]])
#----------------------------------------------------


가구별 전력량 예측용 데이터의 경우

max_prediction_length = 24  # 이전 7일을 보고 1일 후를 예측
max_encoder_length = 7*24
# 마지막 하루(24시간) 빼고 학습
training_cutoff = time_df["hours_from_start"].max() - max_prediction_length

training = TimeSeriesDataSet(
    time_df[lambda x: x.hours_from_start <= training_cutoff],
    time_idx="hours_from_start",
    target="power_usage",
    group_ids=["consumer_id"],  # group이 여러개
    min_encoder_length=max_encoder_length // 2,
    max_encoder_length=max_encoder_length,
    min_prediction_length=1,
    max_prediction_length=max_prediction_length,
    static_categoricals=["consumer_id"],  # 고객 id는 불변
    time_varying_known_reals=["hours_from_start","day","day_of_week", "month", 'hour'],
    time_varying_unknown_reals=['power_usage'],
    # 정규화하기 전에 softplus변환 후에 정규화 실행(log/logp1/logit/relu등 있음)
    # 각 group별로 정규화. group이 여러개 있고, 크기 범위가 다르다.
    target_normalizer=GroupNormalizer(
        groups=["consumer_id"], transformation="softplus"
    ),  # we normalize by group
    add_relative_time_idx=True,
    add_target_scales=True,
    add_encoder_length=True,
)

validation = TimeSeriesDataSet.from_dataset(training, time_df,
predict=True, stop_randomization=True)

# create dataloaders for  our model
batch_size = 64
# to_dataloader를 통해, torch의 dataloader처럼 동작함
# if you have a strong GPU, feel free to increase the number of workers  
train_dataloader = training.to_dataloader(train=True, batch_size=batch_size, num_workers=0)
val_dataloader = validation.to_dataloader(train=False, batch_size=batch_size * 10, num_workers=0)



[References]

1. Medium blog TFT: https://towardsdatascience.com/temporal-fusion-transformer-time-series-forecasting-with-deep-learning-complete-tutorial-d32c1e51cd91

2. [2023년 4월]

-XGBoost, LightGBM: https://www.youtube.com/watch?v=4Jz4_IOgS4c

-WRN 코드: https://github.com/creinders/ChimeraMix/tree/main/models

-데이터 분석-클리닝: https://double-d.tistory.com/m/14

-데이터 정제와 정규화-사이킷런 기초: https://cyan91.tistory.com/m/40

-Data cleaning in 5 easy steps+Examples: 

https://www.iteratorshq.com/blog/data-cleaning-in-5-easy-steps/

-우리가 pytorch lightning을 써야 하는 이유: 

https://baeseongsu.github.io/posts/pytorch-lightning-introduction/

-트랜스포머 이해 굿: https://www.youtube.com/watch?v=AA621UofTUA

-OpenRefine 툴: https://www.youtube.com/watch?v=nORS7STbLyk / https://www.youtube.com/watch?v=oRH-1RG8oQY

-TFT 적용 예제: https://github.com/IKKIM00/stock-and-pm2.5-prediction-using-TFT / https://dacon.io/competitions/official/235736/data