File size: 8,349 Bytes
9c5a6d0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98c087e
 
9c5a6d0
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
import os
import gradio as gr
import pandas as pd
import numpy as np
from typing import Dict, List

from openai import OpenAI
from dotenv import load_dotenv

# Load environment variables
load_dotenv()


# 1) Вкажіть свій OpenAI ключ
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))


##############################################################################
# 1. Вихідні дані: JSON із "хінтами"
##############################################################################
classes_json = {
    "Pain": [
        "ache", "aches", "hurts", "pain", "painful", "sore"
        # ...
    ],
    "Chest pain": [
        "aches in my chest", "chest pain", "chest hurts", "sternum pain"
    ],
    "Physical Activity": [
        "exercise", "walking", "running", "biking"
    ],
    "Office visit": [
        "appointment scheduled", "annual checkup", "office visit"
    ],
    # ...
}

##############################################################################
# 2. Глобальні змінні (спрощено)
##############################################################################
df = None
embeddings = None
class_signatures = None

##############################################################################
# 3. Функція для завантаження даних
##############################################################################
def load_data(csv_path: str = "messages.csv", emb_path: str = "embeddings.npy"):
    global df, embeddings
    df_local = pd.read_csv(csv_path)
    emb_local = np.load(emb_path)
    assert len(df_local) == len(emb_local), "CSV і embeddings різної довжини!"

    df_local["Target"] = "Unlabeled"

    # Нормалізація embeddings
    emb_local = (emb_local - emb_local.mean(axis=0)) / emb_local.std(axis=0)

    df = df_local
    embeddings = emb_local

##############################################################################
# 4. Виклик OpenAI для отримання одного embedding
##############################################################################
def get_openai_embedding(text: str, model_name: str = "text-embedding-3-small") -> list:
    response = client.embeddings.create(
        input=text,
        model=model_name
    )
    return response.data[0].embedding

##############################################################################
# 5. Отримати embeddings для списку фраз (хінтів) і усереднити
##############################################################################
def embed_hints(hint_list: List[str], model_name: str) -> np.ndarray:
    emb_list = []
    for hint in hint_list:
        emb = get_openai_embedding(hint, model_name=model_name)
        emb_list.append(emb)
    return np.array(emb_list, dtype=np.float32)

##############################################################################
# 6. Будуємо signatures для кожного класу
##############################################################################
def build_class_signatures(model_name: str):
    global class_signatures
    signatures = {}
    for cls_name, hints in classes_json.items():
        if not hints:
            continue
        arr = embed_hints(hints, model_name=model_name)
        signatures[cls_name] = arr.mean(axis=0)
    class_signatures = signatures
    return "Signatures побудовано!"

##############################################################################
# 7. Функція класифікації одного рядка (dot product)
##############################################################################
def predict_class(text_embedding: np.ndarray, signatures: Dict[str, np.ndarray]) -> str:
    best_label = "Unknown"
    best_score = float("-inf")
    for cls, sign in signatures.items():
        score = np.dot(text_embedding, sign)
        if score > best_score:
            best_score = score
            best_label = cls
    return best_label

##############################################################################
# 8. Класифікація відфільтрованих рядків
##############################################################################
def classify_rows(filter_substring: str):
    global df, embeddings, class_signatures

    if class_signatures is None:
        return "Спочатку збудуйте signatures!"

    if df is None or embeddings is None:
        return "Дані не завантажені! Спочатку викличте load_data."

    if filter_substring:
        filtered_idx = df[df["Message"].str.contains(filter_substring, case=False, na=False)].index
    else:
        filtered_idx = df.index

    for i in filtered_idx:
        emb_vec = embeddings[i]
        pred = predict_class(emb_vec, class_signatures)
        df.at[i, "Target"] = pred

    result_df = df.loc[filtered_idx, ["Message", "Target"]].copy()
    return result_df.reset_index(drop=True)

##############################################################################
# 9. Збереження CSV
##############################################################################
def save_data():
    global df
    if df is None:
        return "Дані відсутні!"
    df.to_csv("messages_with_labels.csv", index=False)
    return "Файл 'messages_with_labels.csv' збережено!"

##############################################################################
# 10. Gradio UI
##############################################################################
def ui_load_data(csv_path, emb_path):
    load_data(csv_path, emb_path)
    return f"Data loaded from {csv_path} and {emb_path}. Rows: {len(df)}"

def ui_build_signatures(model_name):
    msg = build_class_signatures(model_name)
    return msg

def ui_classify_data(filter_substring):
    result = classify_rows(filter_substring)
    if isinstance(result, str):
        return result
    return result

def ui_save_data():
    return save_data()

def main():
    import gradio as gr

    with gr.Blocks() as demo:
        gr.Markdown("# SDC Classifier з Gradio")
        gr.Markdown("## 1) Завантаження даних")

        with gr.Row():
            csv_input = gr.Textbox(value="messages.csv", label="CSV-файл")
            emb_input = gr.Textbox(value="embeddings.npy", label="Numpy Embeddings")
            load_btn = gr.Button("Load data")

        load_output = gr.Label(label="Loading result")

        load_btn.click(fn=ui_load_data, inputs=[csv_input, emb_input], outputs=load_output)

        gr.Markdown("## 2) Побудова Class Signatures")
        # openai_key_in = gr.Textbox(label="OpenAI API Key", type="password")
        model_choice = gr.Dropdown(choices=["text-embedding-3-large","text-embedding-3-small"],
                                   value="text-embedding-3-small", label="OpenAI model")
        build_btn = gr.Button("Build signatures")
        build_out = gr.Label(label="Signatures")

        build_btn.click(fn=ui_build_signatures, inputs=[model_choice], outputs=build_out)

        gr.Markdown("## 3) Класифікація")
        filter_in = gr.Textbox(label="Filter substring (optional)")
        classify_btn = gr.Button("Classify")
        classify_out = gr.Dataframe(label="Result (Message / Target)")

        classify_btn.click(fn=ui_classify_data, inputs=[filter_in], outputs=[classify_out])

        gr.Markdown("## 4) Зберегти CSV")
        save_btn = gr.Button("Save labeled data")
        save_out = gr.Label()

        save_btn.click(fn=ui_save_data, inputs=[], outputs=save_out)

        gr.Markdown("""
        ### Опис:
        1. Натисніть 'Load data', щоб завантажити ваші дані (CSV + embeddings).
        2. Укажіть OpenAI API модель, натисніть 'Build signatures'.
        3. Вкажіть фільтр (необов'язково), натисніть 'Classify'. 
           Отримаєте таблицю з полем Target. 
        4. 'Save labeled data' збереже 'messages_with_labels.csv'.
        """)
    
    demo.launch(server_name="0.0.0.0", server_port=7860, share=True)
    # demo.launch()


if __name__ == "__main__":
    main()