-
Notifications
You must be signed in to change notification settings - Fork 2
[AI][FEAT] TCN 모델 뼈대 및 학습/추론 어댑터 구현 #291
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
bb10575
[AI][FEAT]: TCN 모델 뼈대 및 학습/추론 어댑터 구현
KIMSE0NG1L fda87a1
[AI][FEAT]: TCN 모델 뼈대 및 학습/추론 어댑터 구현
KIMSE0NG1L b7af576
Merge branch 'feat/SISC-290-TCN-model' of https://github.com/SISC-IT/…
twq110 3d5359c
[AI] SISC-290 [FIX] TCN wrapper 최적화(중복 루프 제거)
twq110 762b34a
[AI] SISC-290 [FIX] TCN wrapper 기술적 지표 레거시 버전 사용 픽스
twq110 30a5127
[AI] SISC-290 [FEAT] TCN 모델 래퍼 텐서 학습 코드 추가
twq110 6937d90
[AI] SISC-290 [FEAT] TCN train 데이터셋 인덱스 슬라이싱을 날짜단위로 처리하도록 변경
twq110 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,25 +1,53 @@ | ||
| # AI/modules/signal/core/dataset_builder.py | ||
| import pandas as pd | ||
| from typing import Union, Optional | ||
|
|
||
| from AI.modules.signal.core.data_loader import DataLoader | ||
| from AI.modules.features.technical import add_technical_indicators | ||
| from AI.modules.features.market_derived import add_macro_features | ||
| from AI.modules.features.processor import FeatureProcessor | ||
| from AI.modules.features.market_derived import add_market_changes, add_macro_changes | ||
|
|
||
| def get_standard_training_data(start_date: str, end_date: str) -> pd.DataFrame: | ||
| def apply_strict_nan_rules(df: pd.DataFrame) -> pd.DataFrame: | ||
| """ | ||
| 데이터 수집/전처리 코드를 짤 필요 없이 이 함수만 호출하면 됩니다. | ||
| 명세서에 정의된 모든 원천/파생 피처가 포함된 DataFrame을 반환합니다. | ||
| SISC 데이터 명세서에 따른 엄격한 결측치 처리 규칙을 적용합니다. | ||
| """ | ||
| # 1. DB에서 원천 데이터(Raw) 로드 | ||
| loader = DataLoader() | ||
| raw_df = loader.load_data_from_db(start_date, end_date) | ||
| df_clean = df.copy() | ||
|
|
||
| macro_cols = ['vix_close', 'vix_change_rate', 'us10y', 'us10y_chg', 'dxy_close', 'dxy_chg'] | ||
| available_macro = [col for col in macro_cols if col in df_clean.columns] | ||
|
|
||
| if available_macro: | ||
| df_clean[available_macro] = df_clean[available_macro].ffill() | ||
|
|
||
| df_clean = df_clean.dropna().reset_index(drop=True) | ||
| return df_clean | ||
|
|
||
| def get_standard_training_data( | ||
| start_date_or_df: Union[str, pd.DataFrame], | ||
| end_date: Optional[str] = None | ||
| ) -> pd.DataFrame: | ||
| """ | ||
| SISC 파이프라인 표준 학습 데이터셋 생성기. (학습/추론 겸용) | ||
| - 사용법 1 (학습용): get_standard_training_data('2020-01-01', '2024-01-01') -> DB 조회 | ||
| - 사용법 2 (추론용): get_standard_training_data(df) -> DB 생략하고 전처리만 수행 | ||
| """ | ||
| # 1. 입력 타입에 따른 분기 처리 (DB 로드 vs 직접 주입) | ||
| if isinstance(start_date_or_df, pd.DataFrame): | ||
| df = start_date_or_df.copy() | ||
| else: | ||
| loader = DataLoader() | ||
| df = loader.load_data_from_db(start_date_or_df, end_date) | ||
| if df is None or df.empty: | ||
| raise ValueError(f"지정된 기간({start_date_or_df} ~ {end_date})의 데이터를 불러오지 못했습니다.") | ||
|
|
||
| # 2. 파생 피처 레이어 계산 (1차: 기초 변화율 연산) | ||
| df = add_market_changes(df) | ||
| df = add_macro_changes(df) | ||
|
|
||
| # 2. 파생 피처 레이어 계산 | ||
| # (팀장님이 기존 features 모듈을 활용해 모든 지표를 미리 계산해서 붙여줌) | ||
| df = add_technical_indicators(raw_df) # rsi_14, macd 등 추가 | ||
| df = add_macro_features(df) # vix_z_score, us10y_chg 등 추가 | ||
| # 3. 파생 피처 레이어 계산 (2차: FeatureProcessor를 통한 심화 지표 일괄 연산) | ||
| processor = FeatureProcessor(df) | ||
| df = processor.execute_pipeline() | ||
|
|
||
| # 3. 결측치(NaN) 처리 | ||
| # Market은 Drop, Macro는 ffill 등... | ||
| # 4. 결측치(NaN) 처리 규칙 적용 | ||
| df = apply_strict_nan_rules(df) | ||
|
|
||
| return df | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| import torch | ||
| import torch.nn as nn | ||
| from typing import List | ||
|
|
||
|
|
||
| class Chomp1d(nn.Module): | ||
| # Causal padding 뒤에 생기는 미래 시점 누수를 잘라냅니다. | ||
| def __init__(self, chomp_size: int): | ||
| super().__init__() | ||
| self.chomp_size = chomp_size | ||
|
|
||
| def forward(self, x: torch.Tensor) -> torch.Tensor: | ||
| if self.chomp_size == 0: | ||
| return x | ||
| return x[:, :, :-self.chomp_size].contiguous() | ||
|
|
||
|
|
||
| class TemporalBlock(nn.Module): | ||
| # 두 개의 dilated Conv1d와 residual connection으로 TCN의 기본 블록을 구성합니다. | ||
| def __init__( | ||
| self, | ||
| in_channels: int, | ||
| out_channels: int, | ||
| kernel_size: int, | ||
| dilation: int, | ||
| dropout: float, | ||
| ): | ||
| super().__init__() | ||
| padding = (kernel_size - 1) * dilation | ||
|
|
||
| self.net = nn.Sequential( | ||
| nn.Conv1d( | ||
| in_channels, | ||
| out_channels, | ||
| kernel_size, | ||
| padding=padding, | ||
| dilation=dilation, | ||
| ), | ||
| Chomp1d(padding), | ||
| nn.ReLU(), | ||
| nn.Dropout(dropout), | ||
| nn.Conv1d( | ||
| out_channels, | ||
| out_channels, | ||
| kernel_size, | ||
| padding=padding, | ||
| dilation=dilation, | ||
| ), | ||
| Chomp1d(padding), | ||
| nn.ReLU(), | ||
| nn.Dropout(dropout), | ||
| ) | ||
| self.downsample = ( | ||
| nn.Conv1d(in_channels, out_channels, kernel_size=1) | ||
| if in_channels != out_channels | ||
| else None | ||
| ) | ||
| self.activation = nn.ReLU() | ||
|
|
||
| def forward(self, x: torch.Tensor) -> torch.Tensor: | ||
| residual = x if self.downsample is None else self.downsample(x) | ||
| return self.activation(self.net(x) + residual) | ||
|
|
||
|
|
||
| class TCNClassifier(nn.Module): | ||
| # 여러 개의 TemporalBlock을 쌓아 멀티 호라이즌 이진 분류 logits를 출력합니다. | ||
| def __init__( | ||
| self, | ||
| input_size: int, | ||
| output_size: int, | ||
| num_channels: List[int], | ||
| kernel_size: int = 3, | ||
| dropout: float = 0.2, | ||
| ): | ||
| super().__init__() | ||
|
|
||
| layers = [] | ||
| for i, out_channels in enumerate(num_channels): | ||
| in_channels = input_size if i == 0 else num_channels[i - 1] | ||
| dilation = 2 ** i | ||
| layers.append( | ||
| TemporalBlock( | ||
| in_channels=in_channels, | ||
| out_channels=out_channels, | ||
| kernel_size=kernel_size, | ||
| dilation=dilation, | ||
| dropout=dropout, | ||
| ) | ||
| ) | ||
|
|
||
| self.backbone = nn.Sequential(*layers) | ||
| self.head = nn.Sequential( | ||
| nn.AdaptiveAvgPool1d(1), | ||
| nn.Flatten(), | ||
| nn.Linear(num_channels[-1], output_size), | ||
| ) | ||
|
|
||
| def forward(self, x: torch.Tensor) -> torch.Tensor: | ||
| # 입력은 [Batch, Seq, Features]이며 Conv1d에 맞게 [Batch, Features, Seq]로 바꿉니다. | ||
| x = x.permute(0, 2, 1) | ||
| x = self.backbone(x) | ||
| return self.head(x) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정렬·종목 분리 전에 변화율을 계산하면 피처가 오염됩니다.
add_market_changes()는shift()/pct_change()에 의존하는데, 지금은FeatureProcessor가 날짜 정렬을 수행하기 전에 먼저 실행됩니다. 게다가load_data_from_db(start_date_or_df, end_date)는tickers를 넘기지 않아 전체 종목을 반환할 수 있어서, 멀티 종목 데이터에서는 다른 종목의 직전 행이prev_close로 섞일 수 있습니다. 최소한 날짜 정렬을 먼저 하고, 멀티 종목 입력이면 ticker 기준으로 분리해서 변화율/롤링 지표를 계산해야 합니다.🧰 Tools
🪛 Ruff (0.15.5)
[warning] 40-40: Avoid specifying long messages outside the exception class
(TRY003)
🤖 Prompt for AI Agents