Update app.py
Browse files
app.py
CHANGED
|
@@ -18,10 +18,10 @@ except (KeyError, AttributeError):
|
|
| 18 |
|
| 19 |
# AI 모델 설정
|
| 20 |
generation_config = {
|
| 21 |
-
"temperature": 0.
|
| 22 |
"top_p": 0.95,
|
| 23 |
"top_k": 40,
|
| 24 |
-
"max_output_tokens":
|
| 25 |
"response_mime_type": "text/plain",
|
| 26 |
}
|
| 27 |
model = genai.GenerativeModel(
|
|
@@ -29,53 +29,58 @@ model = genai.GenerativeModel(
|
|
| 29 |
generation_config=generation_config,
|
| 30 |
)
|
| 31 |
|
| 32 |
-
# --- 2. AI 프롬프트 (
|
| 33 |
-
|
| 34 |
-
당신은 현실적인
|
| 35 |
-
당신의 임무는 사용자가 제공한 변수들의 현실적인 관계를 고려하여, 머신러닝 회귀 분석 학습에 적합한 데이터를 **Markdown 테이블 형식**으로 생성하는 것입니다.
|
| 36 |
|
| 37 |
**사용자 입력:**
|
| 38 |
-
* 원인 (X 변수)
|
| 39 |
-
* 결과 (Y 변수)
|
| 40 |
* 생성할 데이터 개수: {num_rows}
|
| 41 |
|
| 42 |
**수행할 작업 (매우 중요):**
|
| 43 |
-
1. **현실적 관계 모델링:**
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
|
|
|
|
|
|
| 48 |
4. **출력 형식 준수:** 결과는 **오직 Markdown 테이블 형식**으로만 출력합니다.
|
| 49 |
|
| 50 |
**출력 형식 (절대 변경 금지):**
|
| 51 |
-
* 첫 줄은 헤더
|
| 52 |
-
* 두 번째 줄은 구분선
|
| 53 |
-
* 그 이후로는 `| 값1 | 값2 |` 형식의 데이터 행을 {num_rows}개 만큼 생성합니다.
|
| 54 |
* 설명, 코드, ``` 등 다른 어떤 텍스트도 포함하지 마세요.
|
| 55 |
|
| 56 |
**현실적인 데이터 출력 예시 (규칙을 잘 따르는 예시):**
|
| 57 |
-
| 공부 시간 | 시험 점수 |
|
| 58 |
-
|
| 59 |
-
|
|
| 60 |
-
|
|
| 61 |
-
|
|
| 62 |
-
| 3.
|
| 63 |
-
| 5.
|
| 64 |
-
| 6.5 | 98.1 |
|
| 65 |
-
| 1.8 | 75.3 |
|
| 66 |
-
| 4.2 | 92.0 |
|
| 67 |
|
| 68 |
이제 아래 정보를 바탕으로 현실적인 제약 조건을 따른 Markdown 테이블을 생성해주세요.
|
| 69 |
"""
|
| 70 |
|
| 71 |
# --- 3. 핵심 기능 함수 ---
|
| 72 |
|
| 73 |
-
def generate_markdown_data(
|
| 74 |
-
"""AI를 호출하여 Markdown 테이블 형식의 데이터를 받아오는 함수"""
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
)
|
| 80 |
try:
|
| 81 |
response = model.generate_content([prompt])
|
|
@@ -87,21 +92,25 @@ def generate_markdown_data(x_name, y_name, num_rows):
|
|
| 87 |
def parse_markdown_to_df(markdown_text):
|
| 88 |
"""Markdown 테이블 텍스트를 Pandas DataFrame으로 변환하는 함수"""
|
| 89 |
try:
|
| 90 |
-
|
| 91 |
-
|
|
|
|
|
|
|
| 92 |
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
[cell.strip() for cell in row.split('|')]
|
| 96 |
-
for row in cleaned_lines[1:]
|
| 97 |
-
]
|
| 98 |
-
|
| 99 |
-
df = pd.DataFrame(data, columns=header)
|
| 100 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
for col in df.columns:
|
| 102 |
-
|
| 103 |
-
|
|
|
|
|
|
|
| 104 |
df.dropna(inplace=True)
|
|
|
|
| 105 |
return df
|
| 106 |
|
| 107 |
except Exception as e:
|
|
@@ -113,8 +122,8 @@ def parse_markdown_to_df(markdown_text):
|
|
| 113 |
|
| 114 |
# --- 4. Streamlit UI 구성 ---
|
| 115 |
colored_header(
|
| 116 |
-
label="🤖 나만의 AI 학습 데이터 만들기 (회귀용)",
|
| 117 |
-
description="AI가
|
| 118 |
color_name="blue-70",
|
| 119 |
)
|
| 120 |
add_vertical_space(1)
|
|
@@ -127,20 +136,20 @@ if 'markdown_output' not in st.session_state:
|
|
| 127 |
|
| 128 |
# 입력 영역
|
| 129 |
st.subheader("1. 데이터 규칙 정하기")
|
| 130 |
-
col1, col2, col3 = st.columns(
|
| 131 |
|
| 132 |
with col1:
|
| 133 |
-
|
| 134 |
-
"**원인(X) 이름은?**",
|
| 135 |
-
value="공부
|
| 136 |
-
help="데이터의 원인이 되는 값의
|
| 137 |
)
|
| 138 |
|
| 139 |
with col2:
|
| 140 |
-
|
| 141 |
-
"**결과(Y) 이름은?**",
|
| 142 |
-
value="시험 점수",
|
| 143 |
-
help="원인(X)에 따라 변하는 결과 값의
|
| 144 |
)
|
| 145 |
|
| 146 |
with col3:
|
|
@@ -148,7 +157,7 @@ with col3:
|
|
| 148 |
"**데이터는 몇 개 만들까요?**",
|
| 149 |
min_value=10,
|
| 150 |
max_value=200,
|
| 151 |
-
value=
|
| 152 |
step=10,
|
| 153 |
help="AI를 학습시키려면 데이터가 충분해야 해요. 10개 이상을 추천해요!"
|
| 154 |
)
|
|
@@ -160,16 +169,20 @@ add_vertical_space(2)
|
|
| 160 |
|
| 161 |
# 실행 및 결과 출력 영역
|
| 162 |
if generate_button:
|
| 163 |
-
|
| 164 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
else:
|
| 166 |
-
with st.spinner("똑똑한 AI가
|
| 167 |
-
markdown_data = generate_markdown_data(
|
| 168 |
|
| 169 |
if markdown_data:
|
| 170 |
df = parse_markdown_to_df(markdown_data)
|
| 171 |
|
| 172 |
-
if df is not None:
|
| 173 |
st.session_state.generated_df = df
|
| 174 |
st.session_state.markdown_output = markdown_data
|
| 175 |
st.success("🎉 데이터 생성 완료! 아래에서 확인하고 다운로드하세요.")
|
|
@@ -182,29 +195,24 @@ if generate_button:
|
|
| 182 |
|
| 183 |
# 세션 상태에 저장된 데이터가 있으면 화면에 표시
|
| 184 |
if st.session_state.generated_df is not None:
|
| 185 |
-
st.
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
try:
|
| 190 |
-
y_col_name = st.session_state.generated_df.columns[1]
|
| 191 |
-
max_val = st.session_state.generated_df[y_col_name].max()
|
| 192 |
-
min_val = st.session_state.generated_df[y_col_name].min()
|
| 193 |
-
st.info(f"**결과값('{y_col_name}')의 범위:** 최소 {min_val:.1f} ~ 최대 {max_val:.1f}")
|
| 194 |
-
except (IndexError, TypeError):
|
| 195 |
-
# DataFrame이 비어있거나 숫자형이 아닐 경우를 대비한 예외 처리
|
| 196 |
-
pass
|
| 197 |
|
|
|
|
|
|
|
|
|
|
| 198 |
|
| 199 |
-
st.subheader("
|
| 200 |
st.markdown("이 버튼을 눌러 위에 보이는 데이터를 CSV 파일로 컴퓨터에 저장하세요.")
|
| 201 |
|
| 202 |
-
csv =
|
| 203 |
|
| 204 |
st.download_button(
|
| 205 |
label="📥 CSV 파일 다운로드",
|
| 206 |
data=csv,
|
| 207 |
-
file_name=
|
| 208 |
mime="text/csv",
|
| 209 |
use_container_width=True
|
| 210 |
)
|
|
|
|
| 18 |
|
| 19 |
# AI 모델 설정
|
| 20 |
generation_config = {
|
| 21 |
+
"temperature": 0.8, # 다변수 관계의 창의성을 위해 온도를 약간 높임
|
| 22 |
"top_p": 0.95,
|
| 23 |
"top_k": 40,
|
| 24 |
+
"max_output_tokens": 8192, # 더 많은 데이터와 변수를 처리하기 위해 확장
|
| 25 |
"response_mime_type": "text/plain",
|
| 26 |
}
|
| 27 |
model = genai.GenerativeModel(
|
|
|
|
| 29 |
generation_config=generation_config,
|
| 30 |
)
|
| 31 |
|
| 32 |
+
# --- 2. AI 프롬프트 (다변수 지원을 위해 대폭 개선) ---
|
| 33 |
+
MULTI_VARIABLE_PROMPT = """
|
| 34 |
+
당신은 현실적인 다변량 데이터를 시뮬레이션하는 데이터 과학자 AI입니다.
|
| 35 |
+
당신의 임무는 사용자가 제공한 여러 원인(X) 변수와 결과(Y) 변수들의 현실적인 관계를 고려하여, 머신러닝 회귀 분석 학습에 적합한 데이터를 **Markdown 테이블 형식**으로 생성하는 것입니다.
|
| 36 |
|
| 37 |
**사용자 입력:**
|
| 38 |
+
* 원인 (X 변수) 목록: "{x_names_str}"
|
| 39 |
+
* 결과 (Y 변수) 목록: "{y_names_str}"
|
| 40 |
* 생성할 데이터 개수: {num_rows}
|
| 41 |
|
| 42 |
**수행할 작업 (매우 중요):**
|
| 43 |
+
1. **현실적 다변량 관계 모델링:**
|
| 44 |
+
* 제공된 변수들 간의 **현실적인 상관관계**를 모델링합니다. 일부 X는 Y에 긍정적인 영향을, 다른 X는 부정적인 영향을 줄 수 있습니다.
|
| 45 |
+
* X 변수들 사이에도 자연스러운 상관관계가 존재할 수 있습니다. (예: '운동 시간'이 늘면 '수면의 질'도 좋아지는 경향)
|
| 46 |
+
2. **현실적 변동성 추가:** 관계가 완벽한 수학 공식이 아닌, 현실 데이터처럼 보이도록 적절한 무작위 변동성을 추가합니다.
|
| 47 |
+
3. **현실적 제약 조건 적용:** 각 변수의 의미를 고려하여 상한선(Maximum)과 하한선(Minimum)을 자연스럽게 적용합니다.
|
| 48 |
+
* 예를 들어, 변수 이름에 '점수', '만족도', '비율'이 포함되면 **결과값이 100을 넘지 않고, 0 미만이 되지 않도록** 데이터를 생성합니다.
|
| 49 |
+
* 변수 이름에 '시간', '비용', '노력' 등이 포함되면, 이 값이 커질수록 결과값의 상승폭이 점차 둔화되는 **'수확 체감(diminishing returns)' 현상**을 현실적으로 반영합니다.
|
| 50 |
4. **출력 형식 준수:** 결과는 **오직 Markdown 테이블 형식**으로만 출력합니다.
|
| 51 |
|
| 52 |
**출력 형식 (절대 변경 금지):**
|
| 53 |
+
* 첫 줄은 헤더 `{header_line}` 입니다.
|
| 54 |
+
* 두 번째 줄은 구분선 `{separator_line}` 입니다.
|
| 55 |
+
* 그 이후로는 `| 값1 | 값2 | ... |` 형식의 데이터 행을 {num_rows}개 만큼 생성합니다.
|
| 56 |
* 설명, 코드, ``` 등 다른 어떤 텍스트도 포함하지 마세요.
|
| 57 |
|
| 58 |
**현실적인 데이터 출력 예시 (규칙을 잘 따르는 예시):**
|
| 59 |
+
| 공부 시간 | 수면 시간 | 시험 점수 | 컨디션 점수 |
|
| 60 |
+
|---|---|---|---|
|
| 61 |
+
| 1.5 | 8.2 | 65.7 | 88.1 |
|
| 62 |
+
| 4.0 | 6.5 | 88.2 | 72.4 |
|
| 63 |
+
| 0.5 | 7.5 | 42.1 | 81.0 |
|
| 64 |
+
| 3.2 | 8.0 | 85.9 | 92.5 |
|
| 65 |
+
| 5.5 | 5.8 | 91.5 | 65.3 |
|
|
|
|
|
|
|
|
|
|
| 66 |
|
| 67 |
이제 아래 정보를 바탕으로 현실적인 제약 조건을 따른 Markdown 테이블을 생성해주세요.
|
| 68 |
"""
|
| 69 |
|
| 70 |
# --- 3. 핵심 기능 함수 ---
|
| 71 |
|
| 72 |
+
def generate_markdown_data(x_names, y_names, num_rows):
|
| 73 |
+
"""AI를 호출하여 다변량 Markdown 테이블 형식의 데이터를 받아오는 함수"""
|
| 74 |
+
all_vars = x_names + y_names
|
| 75 |
+
header_line = "| " + " | ".join(all_vars) + " |"
|
| 76 |
+
separator_line = "|---" * len(all_vars) + "|"
|
| 77 |
+
|
| 78 |
+
prompt = MULTI_VARIABLE_PROMPT.format(
|
| 79 |
+
x_names_str=", ".join(x_names),
|
| 80 |
+
y_names_str=", ".join(y_names),
|
| 81 |
+
num_rows=num_rows,
|
| 82 |
+
header_line=header_line,
|
| 83 |
+
separator_line=separator_line
|
| 84 |
)
|
| 85 |
try:
|
| 86 |
response = model.generate_content([prompt])
|
|
|
|
| 92 |
def parse_markdown_to_df(markdown_text):
|
| 93 |
"""Markdown 테이블 텍스트를 Pandas DataFrame으로 변환하는 함수"""
|
| 94 |
try:
|
| 95 |
+
# Markdown 테이블을 StringIO를 사용해 가상 파일처럼 읽어들임
|
| 96 |
+
# 이렇게 하면 pandas의 read_csv가 더 안정적으로 파싱할 수 있음
|
| 97 |
+
md_file = io.StringIO(markdown_text)
|
| 98 |
+
df = pd.read_csv(md_file, sep='|', skipinitialspace=True)
|
| 99 |
|
| 100 |
+
# 첫 번째와 마지막 열은 비어있으므로 제거
|
| 101 |
+
df = df.iloc[:, 1:-1]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
|
| 103 |
+
# 헤더 공백 제거
|
| 104 |
+
df.columns = df.columns.str.strip()
|
| 105 |
+
|
| 106 |
+
# 데이터 타입 변환 및 정리
|
| 107 |
for col in df.columns:
|
| 108 |
+
# 첫 번째 행은 '---' 이므로 제거
|
| 109 |
+
df[col] = df[col].iloc[1:]
|
| 110 |
+
df[col] = pd.to_numeric(df[col].str.strip(), errors='coerce')
|
| 111 |
+
|
| 112 |
df.dropna(inplace=True)
|
| 113 |
+
df.reset_index(drop=True, inplace=True)
|
| 114 |
return df
|
| 115 |
|
| 116 |
except Exception as e:
|
|
|
|
| 122 |
|
| 123 |
# --- 4. Streamlit UI 구성 ---
|
| 124 |
colored_header(
|
| 125 |
+
label="🤖 나만의 AI 학습 데이터 만들기 (다변량 회귀용)",
|
| 126 |
+
description="AI가 여러 변수들의 복합적인 관계를 고려하여 '진짜 같은' 학습 데이터를 만들어줘요!",
|
| 127 |
color_name="blue-70",
|
| 128 |
)
|
| 129 |
add_vertical_space(1)
|
|
|
|
| 136 |
|
| 137 |
# 입력 영역
|
| 138 |
st.subheader("1. 데이터 규칙 정하기")
|
| 139 |
+
col1, col2, col3 = st.columns([2, 2, 1])
|
| 140 |
|
| 141 |
with col1:
|
| 142 |
+
x_input_names_str = st.text_area(
|
| 143 |
+
"**원인(X) 변수 이름은?** (쉼표로 구분)",
|
| 144 |
+
value="공부 시간, 수면 시간, 주말 복습 횟수",
|
| 145 |
+
help="데이터의 원인이 되는 값의 이름들을 쉼표(,)로 구분하여 입력하세요."
|
| 146 |
)
|
| 147 |
|
| 148 |
with col2:
|
| 149 |
+
y_input_names_str = st.text_area(
|
| 150 |
+
"**결과(Y) 변수 이름은?** (쉼표로 구분)",
|
| 151 |
+
value="시험 점수, 과제 점수",
|
| 152 |
+
help="원인(X)에 따라 변하는 결과 값의 이름들을 쉼표(,)로 구분하여 입력하세요."
|
| 153 |
)
|
| 154 |
|
| 155 |
with col3:
|
|
|
|
| 157 |
"**데이터는 몇 개 만들까요?**",
|
| 158 |
min_value=10,
|
| 159 |
max_value=200,
|
| 160 |
+
value=50,
|
| 161 |
step=10,
|
| 162 |
help="AI를 학습시키려면 데이터가 충분해야 해요. 10개 이상을 추천해요!"
|
| 163 |
)
|
|
|
|
| 169 |
|
| 170 |
# 실행 및 결과 출력 영역
|
| 171 |
if generate_button:
|
| 172 |
+
# 입력값을 리스트로 변환
|
| 173 |
+
x_names_list = [name.strip() for name in x_input_names_str.split(',') if name.strip()]
|
| 174 |
+
y_names_list = [name.strip() for name in y_input_names_str.split(',') if name.strip()]
|
| 175 |
+
|
| 176 |
+
if not x_names_list or not y_names_list:
|
| 177 |
+
st.warning("⚠️ '원인(X) 변수'와 '결과(Y) 변수'를 하나 이상씩 입력해주세요!")
|
| 178 |
else:
|
| 179 |
+
with st.spinner("똑똑한 AI가 변수들의 복합적인 관계를 생각하며 데이터를 만들고 있어요... 🤖"):
|
| 180 |
+
markdown_data = generate_markdown_data(x_names_list, y_names_list, num_data_rows)
|
| 181 |
|
| 182 |
if markdown_data:
|
| 183 |
df = parse_markdown_to_df(markdown_data)
|
| 184 |
|
| 185 |
+
if df is not None and not df.empty:
|
| 186 |
st.session_state.generated_df = df
|
| 187 |
st.session_state.markdown_output = markdown_data
|
| 188 |
st.success("🎉 데이터 생성 완료! 아래에서 확인하고 다운로드하세요.")
|
|
|
|
| 195 |
|
| 196 |
# 세션 상태에 저장된 데이터가 있으면 화면에 표시
|
| 197 |
if st.session_state.generated_df is not None:
|
| 198 |
+
df_to_show = st.session_state.generated_df
|
| 199 |
+
|
| 200 |
+
st.subheader("2. 생성된 데이터 미리보기")
|
| 201 |
+
st.dataframe(df_to_show)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
|
| 203 |
+
st.subheader("3. 데이터 통계 요약")
|
| 204 |
+
st.markdown("생성된 데이터의 평균, 표준편차, 최소/최대값 등 기술 통계를 확인해보세요.")
|
| 205 |
+
st.dataframe(df_to_show.describe())
|
| 206 |
|
| 207 |
+
st.subheader("4. CSV 파일로 다운로드하기")
|
| 208 |
st.markdown("이 버튼을 눌러 위에 보이는 데이터를 CSV 파일로 컴퓨터에 저장하세요.")
|
| 209 |
|
| 210 |
+
csv = df_to_show.to_csv(index=False).encode('utf-8-sig')
|
| 211 |
|
| 212 |
st.download_button(
|
| 213 |
label="📥 CSV 파일 다운로드",
|
| 214 |
data=csv,
|
| 215 |
+
file_name="ai_generated_multivariable_data.csv",
|
| 216 |
mime="text/csv",
|
| 217 |
use_container_width=True
|
| 218 |
)
|