{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import re\n", "from urllib.parse import urlparse, parse_qs" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import tldextract" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "train = pd.read_csv(\"../../train.csv\")" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "train[\"URL\"] = train[\"URL\"].str.replace(\"[.]\", \".\", regex=False)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "import re\n", "from urllib.parse import urlparse, parse_qs\n", "import tldextract\n", "import zlib\n", "from collections import Counter\n", "import math\n", "import ipaddress\n", "\n", "# def is_ip_address(url):\n", "# if not url or not isinstance(url, str):\n", "# return False\n", "# try:\n", "# parsed = urlparse(url if '//' in url else 'http://' + url)\n", "# domain = parsed.netloc.split(':')[0]\n", "# if not domain:\n", "# return False\n", "# ipaddress.ip_address(domain)\n", "# return True\n", "# except ValueError:\n", "# return False\n", "# except Exception:\n", "# return False\n", "\n", "\n", "\n", "def url_is_whitelisted(url):\n", " trusted_domains = [\n", " # 1. 포털 / 검색엔진\n", " 'naver.com', 'daum.net', 'google.com', 'bing.com', 'yahoo.com',\n", "\n", " # 2. 소셜 미디어 / 커뮤니케이션\n", " 'facebook.com', 'instagram.com', 'twitter.com', 'x.com', 'linkedin.com',\n", " 'whatsapp.com', 'kakao.com', 'kakaocorp.com',\n", "\n", " # 3. 동영상 / 스트리밍\n", " 'youtube.com', 'netflix.com', 'twitch.tv', 'tving.com', 'watcha.com',\n", "\n", " # 4. 쇼핑 / 이커머스\n", " 'amazon.com', 'gmarket.co.kr', '11st.co.kr', 'coupang.com', 'ssg.com', 'wemakeprice.com',\n", "\n", " # 5. 금융 / 결제\n", " 'paypal.com', 'kbfg.com', 'shinhan.com', 'hanafn.com', 'wooribank.com', \n", " 'kakaobank.com', 'toss.im',\n", "\n", " # 6. 공공기관 / 교육\n", " 'gov.kr', 'moe.go.kr', 'epeople.go.kr', 'pusan.ac.kr', 'ac.kr', \n", "\n", " # 7. IT / 기술\n", " 'apple.com', 'microsoft.com', 'adobe.com', 'github.com', 'stackoverflow.com'\n", "]\n", " \n", " try:\n", " domain = urlparse(url if '//' in url else '//' + url).netloc.lower()\n", " for trusted in trusted_domains:\n", " if domain.endswith(trusted):\n", " return True\n", " return False\n", " except:\n", " return False\n", "\n", "\n", "\n", "def check_similar_brand(url):\n", " # 자주 사용되는 브랜드/도메인 목록\n", " common_brands = {\n", " 'google', 'facebook', 'amazon', 'microsoft', 'apple', \n", " 'netflix', 'paypal', 'twitter', 'instagram', 'linkedin',\n", " 'youtube', 'yahoo', 'gmail', 'whatsapp', 'tiktok',\n", " 'geocities', 'angelfire', 'newadvent', 'wikipedia',\n", " }\n", " \n", " # 2. 유사 브랜드 확인\n", " try:\n", " # URL 파싱\n", " parsed = urlparse(url if '//' in url else '//' + url)\n", " domain = parsed.netloc.lower() if parsed.netloc else url.lower()\n", " \n", " for brand in common_brands:\n", " if brand not in domain:\n", " similar = False\n", " # 비슷한 철자 패턴 확인\n", " patterns = [\n", " brand.replace('o', '0'),\n", " brand.replace('i', '1'),\n", " brand.replace('l', '1'),\n", " brand.replace('e', '3'),\n", " brand.replace('a', '4'),\n", " brand.replace('s', '5'),\n", " brand + '-',\n", " brand + '_',\n", " brand[:-1], # 마지막 문자 제거\n", " ''.join(c + c for c in brand), # 문자 중복\n", " ]\n", " \n", " for pattern in patterns:\n", " if pattern in domain:\n", " similar = True\n", " break\n", " \n", " if similar:\n", " return True # 유사 브랜드가 발견되면 True 반환\n", " \n", " except Exception as e:\n", " return False # 예외 발생 시 False 반환\n", " \n", " return False # 유사 브랜드가 없으면 False 반환\n", "\n", "\n", "\n", "# url 압축 비율 계산 함수\n", "def compression_ratio(url: str) -> float:\n", " if not url:\n", " return 0.0\n", " original_length = len(url.encode('utf-8'))\n", " compressed_data = zlib.compress(url.encode('utf-8'))\n", " compressed_length = len(compressed_data)\n", " return compressed_length / original_length\n", "\n", "\n", "def extract_features(url):\n", " parsed_url = urlparse(url)\n", " suspicious_keywords = [\n", " 'login', 'verify', 'account', 'update', 'secure', 'banking', \n", " 'paypal', 'confirm', 'signin', 'auth', 'redirect', 'free', \n", " 'bonus', 'admin', 'support', 'server', 'password', 'click', \n", " 'urgent', 'immediate', 'alert', 'security', 'prompt'\n", " ]\n", " \n", " additional_keywords = [\n", " 'verify', 'wallet', 'cryptocurrency', 'bitcoin', 'ethereum',\n", " 'validation', 'authenticate', 'reset', 'recover', 'access',\n", " 'limited', 'offer', 'prize', 'win', 'winner', 'payment',\n", " 'bank', 'credit', 'debit', 'card', 'expire', 'suspension',\n", " 'unusual', 'activity', 'verify', 'document', 'invoice'\n", " ]\n", " \n", " all_keywords = list(set(suspicious_keywords + additional_keywords))\n", "\n", " contains_keyword = 0\n", " keyword_count = 0\n", " for keyword in all_keywords:\n", " if re.search(r'\\b' + keyword + r'\\b', url, re.IGNORECASE):\n", " contains_keyword = 1\n", " keyword_count += 1\n", " \n", " url_length = len(url)\n", " extracted = tldextract.extract(url)\n", " tld = extracted.suffix\n", " domain = extracted.domain\n", " subdomain = extracted.subdomain\n", "\n", " tld_length = len(tld) if tld else 0\n", " common_tlds = ['com', 'org', 'net', 'edu', 'gov', 'mil', 'io', 'co', 'info', 'biz']\n", " is_common_tld = 1 if tld in common_tlds else 0\n", " country_tlds = ['us', 'uk', 'ca', 'au', 'de', 'fr', 'jp', 'cn', 'ru', 'br', 'in', 'it', 'es']\n", " is_country_tld = 1 if tld in country_tlds else 0\n", " suspicious_tlds = ['xyz', 'top', 'club', 'online', 'site', 'icu', 'vip', 'work', 'rest', 'fit']\n", " is_suspicious_tld = 1 if tld in suspicious_tlds else 0\n", " url_shorteners = ['bit.ly', 'tinyurl.com', 'goo.gl', 't.co', 'ow.ly', 'is.gd', 'buff.ly', 'adf.ly', 'tiny.cc']\n", " full_domain = f\"{domain}.{tld}\" if tld else domain\n", " is_shortened = 1 if full_domain in url_shorteners else 0\n", "\n", "\n", " domain_length = len(domain) if domain else 0\n", " has_subdomain = 1 if subdomain else 0\n", " subdomain_length = len(subdomain) if subdomain else 0\n", " subdomain_count = len(subdomain.split('.')) if subdomain else 0 \n", "\n", " path = parsed_url.path\n", " path_length = len(path)\n", " path_depth = path.count('/') if path else 0\n", "\n", " query = parsed_url.query\n", " has_query = 1 if query else 0\n", " query_length = len(query) if query else 0\n", " query_params = parse_qs(query)\n", " query_param_count = len(query_params) if query_params else 0\n", "\n", " has_fragment = 1 if parsed_url.fragment else 0\n", " fragment_length = len(parsed_url.fragment) if parsed_url.fragment else 0\n", " \n", " # Character type ratios\n", " letter_count = sum(c.isalpha() for c in url)\n", " digit_count = sum(c.isdigit() for c in url)\n", " special_char_count = len(re.findall(r'[^a-zA-Z0-9]', url))\n", " \n", " letter_ratio = letter_count / url_length if url_length > 0 else 0\n", " digit_ratio = digit_count / url_length if url_length > 0 else 0\n", " special_char_ratio = special_char_count / url_length if url_length > 0 else 0\n", " \n", " # Character distribution and entropy\n", " if url:\n", " char_counts = Counter(url)\n", " total_chars = len(url)\n", " char_frequencies = {char: count/total_chars for char, count in char_counts.items()}\n", " entropy = -sum(freq * math.log2(freq) for freq in char_frequencies.values())\n", " else:\n", " entropy = 0\n", "\n", "\n", "\n", "\n", "\n", " if url_length <= 13:\n", " url_length_cat = 0 \n", " elif url_length <= 18:\n", " url_length_cat = 1 \n", " elif url_length <= 25:\n", " url_length_cat = 2 \n", " else:\n", " url_length_cat = 3\n", " \n", " if url_is_whitelisted(url):\n", " return {\n", " # 화이트리스트 URL이면 특징값들을 \"정상적\"으로 처리되도록 설정\n", " \"url_length_cat\": 1,\n", " \"num_dots\": 1,\n", " \"num_digits\": 0,\n", " \"num_special_chars\": 1,\n", " \"url_keyword\": 0,\n", " \"num_underbar\": 0,\n", " \"extract_consecutive_numbers\": 0,\n", " \"number\": 0,\n", " \"upper\": 0,\n", "\n", " \"is_common_tld\": 1,\n", " \"is_country_tld\": 0,\n", " \"is_suspicious_tld\": 0,\n", "\n", " \"domain_length\": 5,\n", " \"has_subdomain\": 0,\n", " \"subdomain_length\": 0,\n", " \"subdomain_count\": 0,\n", "\n", " \"path_depth\": 0,\n", " \"has_query\": 0,\n", " \"query_length\": 0,\n", " \"query_param_count\": 0,\n", " \"url_shorteners\": 0,\n", "\n", " \"compression_ratio\": 1.0,\n", " \"check_similar_brand\": 0,\n", " \"entropy\": 3.0,\n", " \"digit_ratio\": 0.0,\n", " \"special_char_ratio\": 0.1\n", " }\n", " \n", "\n", " return {\n", " \n", " # \"url_length\": url_length,\n", " \"url_length_cat\": url_length_cat,\n", " \"num_dots\": url.count(\".\"),\n", " \"num_digits\": sum(c.isdigit() for c in url),\n", " \"num_special_chars\": len(re.findall(r\"[^a-zA-Z0-9]\", url)),\n", " \"url_keyword\": contains_keyword,\n", " # \"url_keyword_count\": keyword_count,\n", " \"num_underbar\": url.count(\"_\"),\n", " \"extract_consecutive_numbers\": int(bool(re.findall(r'(\\d)\\1+', url))),\n", " \"number\": int(bool(len(re.findall(r'(\\d)(?!\\1)(\\d)(?!\\2)(\\d)', url)))),\n", " \"upper\": int(any(c.isupper() for c in url)),\n", "\n", " \"is_common_tld\": is_common_tld,\n", " \"is_country_tld\": is_country_tld,\n", " \"is_suspicious_tld\": is_suspicious_tld,\n", "\n", " \"domain_length\": domain_length,\n", " \"has_subdomain\": has_subdomain,\n", " \"subdomain_length\": subdomain_length,\n", " \"subdomain_count\": subdomain_count,\n", "\n", " # \"path_length\": path_length,\n", " \"path_depth\": path_depth,\n", " \"has_query\": has_query,\n", " \"query_length\": query_length,\n", " \"query_param_count\": query_param_count,\n", " # \"has_fragment\": has_fragment,\n", " # \"fragment_length\": fragment_length,\n", " \"url_shorteners\": is_shortened,\n", "\n", " # 새로 추가된 특성\n", " \"compression_ratio\": compression_ratio(url),\n", " \"check_similar_brand\" : check_similar_brand(url),\n", " \n", " # Advanced text analysis\n", " \"entropy\": entropy,\n", " #\"letter_ratio\": letter_ratio,\n", " \"digit_ratio\": digit_ratio,\n", " \"special_char_ratio\": special_char_ratio,\n", " \n", " # \"is_ip_address\" : is_ip_address(url)\n", " \n", " }\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def process_chunks(train, chunk_size, extract_features):\n", " \"\"\"\n", " train: DataFrame, 원본 데이터프레임\n", " chunk_size: int, 한 번에 처리할 URL의 개수\n", " extract_features: 함수, 각 URL에 대해 특징을 추출하는 함수\n", " \n", " return: 처리된 DataFrame\n", " \"\"\"\n", " # 청크로 나누기\n", " chunks = [train[\"URL\"][i:i + chunk_size] for i in range(0, len(train), chunk_size)]\n", " \n", " # 각 청크에 대해 apply 및 json_normalize 처리 후 결과를 저장\n", " chunk_results = []\n", " for chunk in chunks:\n", " # 각 청크에 대해 extract_features 함수를 적용\n", " chunk_features = chunk.apply(extract_features)\n", " \n", " # json_normalize 적용\n", " chunk_results.append(pd.json_normalize(chunk_features))\n", " \n", " # 처리된 청크 결과를 합침\n", " return pd.concat(chunk_results, ignore_index=True)\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "train_features_df = process_chunks(train, chunk_size=100000, extract_features=extract_features)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The history saving thread hit an unexpected error (OperationalError('database or disk is full')).History will not be written to the database.\n" ] }, { "data": { "application/vnd.microsoft.datawrangler.viewer.v0+json": { "columns": [ { "name": "index", "rawType": "int64", "type": "integer" }, { "name": "url_length_cat", "rawType": "int64", "type": "integer" }, { "name": "num_dots", "rawType": "int64", "type": "integer" }, { "name": "num_digits", "rawType": "int64", "type": "integer" }, { "name": "num_special_chars", "rawType": "int64", "type": "integer" }, { "name": "url_keyword", "rawType": "int64", "type": "integer" }, { "name": "num_underbar", "rawType": "int64", "type": "integer" }, { "name": "extract_consecutive_numbers", "rawType": "int64", "type": "integer" }, { "name": "number", "rawType": "int64", "type": "integer" }, { "name": "upper", "rawType": "int64", "type": "integer" }, { "name": "is_common_tld", "rawType": "int64", "type": "integer" }, { "name": "is_country_tld", "rawType": "int64", "type": "integer" }, { "name": "is_suspicious_tld", "rawType": "int64", "type": "integer" }, { "name": "domain_length", "rawType": "int64", "type": "integer" }, { "name": "has_subdomain", "rawType": "int64", "type": "integer" }, { "name": "subdomain_length", "rawType": "int64", "type": "integer" }, { "name": "subdomain_count", "rawType": "int64", "type": "integer" }, { "name": "path_depth", "rawType": "int64", "type": "integer" }, { "name": "has_query", "rawType": "int64", "type": "integer" }, { "name": "query_length", "rawType": "int64", "type": "integer" }, { "name": "query_param_count", "rawType": "int64", "type": "integer" }, { "name": "url_shorteners", "rawType": "int64", "type": "integer" }, { "name": "compression_ratio", "rawType": "float64", "type": "float" }, { "name": "check_similar_brand", "rawType": "object", "type": "unknown" }, { "name": "entropy", "rawType": "float64", "type": "float" }, { "name": "digit_ratio", "rawType": "float64", "type": "float" }, { "name": "special_char_ratio", "rawType": "float64", "type": "float" } ], "conversionMethod": "pd.DataFrame", "ref": "d58f565c-1f8f-4d85-8bcd-76ee4a7dd0a1", "rows": [ [ "0", "1", "3", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "6", "0", "0", "0", "0", "0", "0", "0", "0", "1.4444444444444444", "False", "3.3082708345352603", "0.0", "0.16666666666666666" ], [ "1", "2", "3", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "0", "0", "0", "0", "1.4210526315789473", "False", "3.471354487013928", "0.0", "0.15789473684210525" ], [ "2", "2", "2", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0", "15", "0", "0", "0", "0", "0", "0", "0", "0", "1.380952380952381", "False", "3.2728043273346206", "0.0", "0.09523809523809523" ], [ "3", "2", "3", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "0", "0", "0", "0", "1.3636363636363635", "False", "3.5337712560645564", "0.0", "0.13636363636363635" ], [ "4", "3", "2", "2", "5", "1", "0", "0", "0", "1", "0", "0", "0", "7", "1", "6", "1", "2", "0", "0", "0", "0", "1.2424242424242424", "False", "3.772449620842178", "0.06060606060606061", "0.15151515151515152" ], [ "5", "1", "2", "2", "3", "0", "0", "0", "0", "0", "0", "0", "0", "4", "0", "0", "0", "1", "0", "0", "0", "0", "1.4444444444444444", "False", "3.7254805569978675", "0.1111111111111111", "0.16666666666666666" ], [ "6", "3", "2", "0", "8", "0", "0", "0", "0", "0", "0", "0", "0", "2", "0", "0", "0", "4", "0", "0", "0", "0", "1.1041666666666667", "False", "4.3082080733223185", "0.0", "0.16666666666666666" ], [ "7", "0", "2", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "2.0", "False", "2.75", "0.0", "0.25" ], [ "8", "1", "1", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0", "9", "0", "0", "0", "0", "0", "0", "0", "0", "1.5714285714285714", "False", "3.521640636343319", "0.0", "0.07142857142857142" ], [ "9", "3", "4", "12", "5", "0", "0", "0", "1", "0", "0", "0", "0", "15", "0", "0", "0", "1", "0", "0", "0", "0", "1.2962962962962963", "False", "3.9121138909722304", "0.4444444444444444", "0.18518518518518517" ], [ "10", "1", "3", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "1.5", "False", "2.98345859334435", "0.0", "0.1875" ], [ "11", "1", "2", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "0", "0", "0", "0", "1.5714285714285714", "False", "3.182005814760214", "0.0", "0.14285714285714285" ], [ "12", "0", "1", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0", "6", "0", "0", "0", "0", "0", "0", "0", "0", "1.7272727272727273", "False", "2.7321588913645702", "0.0", "0.09090909090909091" ], [ "13", "1", "1", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0", "6", "0", "0", "0", "0", "0", "0", "0", "0", "1.5333333333333334", "False", "3.2402239289418513", "0.0", "0.06666666666666667" ], [ "14", "1", "2", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0", "8", "0", "0", "0", "0", "0", "0", "0", "0", "1.5333333333333334", "False", "3.3232314287976203", "0.0", "0.13333333333333333" ], [ "15", "0", "1", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "1.6666666666666667", "False", "3.2516291673878226", "0.0", "0.08333333333333333" ], [ "16", "2", "3", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "0", "0", "0", "0", "1.3636363636363635", "False", "3.4428621651554656", "0.0", "0.13636363636363635" ], [ "17", "0", "1", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "1.7272727272727273", "False", "3.277613436819116", "0.0", "0.09090909090909091" ], [ "18", "0", "1", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "1.6153846153846154", "False", "3.3927474104487847", "0.0", "0.07692307692307693" ], [ "19", "0", "2", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0", "7", "0", "0", "0", "0", "0", "0", "0", "0", "1.6153846153846154", "False", "3.180832987205441", "0.0", "0.15384615384615385" ], [ "20", "3", "1", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0", "15", "0", "0", "0", "0", "0", "0", "0", "0", "1.3076923076923077", "False", "3.873140679513133", "0.0", "0.038461538461538464" ], [ "21", "1", "3", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "5", "0", "0", "0", "0", "0", "0", "0", "0", "1.5333333333333334", "False", "3.189898095464287", "0.0", "0.2" ], [ "22", "1", "2", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0", "5", "0", "0", "0", "0", "0", "0", "0", "0", "1.5333333333333334", "False", "3.373557262275185", "0.0", "0.13333333333333333" ], [ "23", "2", "3", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "0", "0", "0", "0", "1.2", "False", "3.3174058114255174", "0.0", "0.12" ], [ "24", "3", "3", "0", "7", "0", "2", "0", "0", "0", "0", "0", "0", "6", "0", "0", "0", "2", "0", "0", "0", "0", "1.0714285714285714", "False", "4.005678590456677", "0.0", "0.16666666666666666" ], [ "25", "2", "3", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "0", "0", "0", "0", "1.380952380952381", "False", "3.4992275417233567", "0.0", "0.14285714285714285" ], [ "26", "0", "2", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "1.6153846153846154", "False", "3.3927474104487847", "0.0", "0.15384615384615385" ], [ "27", "3", "3", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "0", "0", "0", "0", "1.2962962962962963", "False", "3.8841550945958057", "0.0", "0.1111111111111111" ], [ "28", "0", "1", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "1.6666666666666667", "False", "3.418295834054489", "0.0", "0.08333333333333333" ], [ "29", "2", "3", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "0", "0", "0", "0", "1.3333333333333333", "False", "3.5535088547976788", "0.0", "0.125" ], [ "30", "2", "3", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "0", "0", "0", "0", "1.4", "False", "3.384183719779189", "0.0", "0.15" ], [ "31", "2", "2", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0", "5", "0", "0", "0", "0", "0", "0", "0", "0", "1.3478260869565217", "False", "3.555533151426996", "0.0", "0.08695652173913043" ], [ "32", "2", "2", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0", "9", "0", "0", "0", "0", "0", "0", "0", "0", "1.4", "False", "3.4841837197791885", "0.0", "0.1" ], [ "33", "0", "1", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "2.0", "False", "2.75", "0.0", "0.125" ], [ "34", "3", "3", "0", "5", "0", "0", "0", "0", "0", "0", "0", "0", "9", "0", "0", "0", "2", "0", "0", "0", "0", "1.2222222222222223", "False", "4.141604167868593", "0.0", "0.1388888888888889" ], [ "35", "3", "1", "3", "5", "0", "0", "0", "0", "0", "0", "0", "0", "18", "0", "0", "0", "0", "0", "0", "0", "0", "1.2258064516129032", "False", "3.897587036123537", "0.0967741935483871", "0.16129032258064516" ], [ "36", "3", "1", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0", "26", "0", "0", "0", "0", "0", "0", "0", "0", "1.1714285714285715", "False", "3.808173561015608", "0.0", "0.05714285714285714" ], [ "37", "2", "1", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0", "12", "0", "0", "0", "0", "0", "0", "0", "0", "1.4", "False", "3.6841837197791887", "0.0", "0.1" ], [ "38", "2", "1", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0", "11", "0", "0", "0", "0", "0", "0", "0", "0", "1.4", "False", "3.446439344671015", "0.0", "0.05" ], [ "39", "1", "2", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "0", "0", "0", "0", "1.5", "False", "3.25", "0.0", "0.125" ], [ "40", "1", "2", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "10", "0", "0", "0", "1", "0", "0", "0", "0", "1.4705882352941178", "False", "3.4548223999466066", "0.0", "0.17647058823529413" ], [ "41", "1", "1", "0", "1", "0", "0", "0", "0", "1", "0", "0", "0", "9", "0", "0", "0", "0", "0", "0", "0", "0", "1.5714285714285714", "False", "3.128085278891395", "0.0", "0.07142857142857142" ], [ "42", "2", "3", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "7", "0", "0", "0", "0", "0", "0", "0", "0", "1.4", "False", "3.2841837197791888", "0.0", "0.15" ], [ "43", "2", "1", "3", "3", "0", "0", "0", "0", "0", "0", "0", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "1.4", "False", "4.1219280948873624", "0.15", "0.15" ], [ "44", "1", "1", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0", "9", "0", "0", "0", "0", "0", "0", "0", "0", "1.5333333333333334", "False", "3.2402239289418517", "0.0", "0.06666666666666667" ], [ "45", "2", "1", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0", "10", "0", "0", "0", "0", "0", "0", "0", "0", "1.4", "False", "3.5841837197791886", "0.0", "0.05" ], [ "46", "0", "2", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "1.6153846153846154", "False", "3.238901256602631", "0.0", "0.15384615384615385" ], [ "47", "2", "3", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "0", "0", "0", "0", "1.4", "False", "3.446439344671015", "0.0", "0.15" ], [ "48", "2", "1", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0", "17", "0", "0", "0", "0", "0", "0", "0", "0", "1.32", "False", "3.3688840705376357", "0.0", "0.08" ], [ "49", "2", "3", "0", "3", "0", "0", "0", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "0", "0", "0", "0", "1.4", "False", "3.6841837197791887", "0.0", "0.15" ] ], "shape": { "columns": 26, "rows": 6995056 } }, "text/html": [ "
| \n", " | url_length_cat | \n", "num_dots | \n", "num_digits | \n", "num_special_chars | \n", "url_keyword | \n", "num_underbar | \n", "extract_consecutive_numbers | \n", "number | \n", "upper | \n", "is_common_tld | \n", "... | \n", "path_depth | \n", "has_query | \n", "query_length | \n", "query_param_count | \n", "url_shorteners | \n", "compression_ratio | \n", "check_similar_brand | \n", "entropy | \n", "digit_ratio | \n", "special_char_ratio | \n", "
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | \n", "1 | \n", "3 | \n", "0 | \n", "3 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "1.444444 | \n", "False | \n", "3.308271 | \n", "0.000000 | \n", "0.166667 | \n", "
| 1 | \n", "2 | \n", "3 | \n", "0 | \n", "3 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "1.421053 | \n", "False | \n", "3.471354 | \n", "0.000000 | \n", "0.157895 | \n", "
| 2 | \n", "2 | \n", "2 | \n", "0 | \n", "2 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "1.380952 | \n", "False | \n", "3.272804 | \n", "0.000000 | \n", "0.095238 | \n", "
| 3 | \n", "2 | \n", "3 | \n", "0 | \n", "3 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "1.363636 | \n", "False | \n", "3.533771 | \n", "0.000000 | \n", "0.136364 | \n", "
| 4 | \n", "3 | \n", "2 | \n", "2 | \n", "5 | \n", "1 | \n", "0 | \n", "0 | \n", "0 | \n", "1 | \n", "0 | \n", "... | \n", "2 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "1.242424 | \n", "False | \n", "3.772450 | \n", "0.060606 | \n", "0.151515 | \n", "
| ... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "
| 6995051 | \n", "0 | \n", "2 | \n", "0 | \n", "2 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "1.800000 | \n", "False | \n", "2.921928 | \n", "0.000000 | \n", "0.200000 | \n", "
| 6995052 | \n", "2 | \n", "2 | \n", "0 | \n", "2 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "1 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "1.400000 | \n", "False | \n", "3.684184 | \n", "0.000000 | \n", "0.100000 | \n", "
| 6995053 | \n", "3 | \n", "2 | \n", "5 | \n", "4 | \n", "0 | \n", "0 | \n", "1 | \n", "0 | \n", "0 | \n", "1 | \n", "... | \n", "2 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "1.100000 | \n", "False | \n", "4.130881 | \n", "0.100000 | \n", "0.080000 | \n", "
| 6995054 | \n", "1 | \n", "1 | \n", "0 | \n", "1 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "1 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "1.571429 | \n", "False | \n", "3.324863 | \n", "0.000000 | \n", "0.071429 | \n", "
| 6995055 | \n", "1 | \n", "1 | \n", "0 | \n", "1 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "1 | \n", "... | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "0 | \n", "1.444444 | \n", "False | \n", "3.614369 | \n", "0.000000 | \n", "0.055556 | \n", "
6995056 rows × 26 columns
\n", "| \n", " | label | \n", "url_length_cat | \n", "num_dots | \n", "num_digits | \n", "num_special_chars | \n", "url_keyword | \n", "num_underbar | \n", "extract_consecutive_numbers | \n", "number | \n", "upper | \n", "... | \n", "subdomain_count | \n", "path_depth | \n", "has_query | \n", "query_length | \n", "query_param_count | \n", "url_shorteners | \n", "compression_ratio | \n", "entropy | \n", "digit_ratio | \n", "special_char_ratio | \n", "
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "... | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "6.995056e+06 | \n", "
| mean | \n", "2.237147e-01 | \n", "1.443553e+00 | \n", "1.546945e+00 | \n", "1.634359e+00 | \n", "2.663572e+00 | \n", "3.707890e-02 | \n", "4.500550e-02 | \n", "5.646374e-02 | \n", "8.128041e-02 | \n", "3.577641e-02 | \n", "... | \n", "2.660177e-01 | \n", "6.056849e-01 | \n", "2.722137e-02 | \n", "1.915589e+00 | \n", "4.228915e-02 | \n", "1.842158e-03 | \n", "1.455253e+00 | \n", "3.536043e+00 | \n", "2.904243e-02 | \n", "1.102289e-01 | \n", "
| std | \n", "4.167331e-01 | \n", "1.116120e+00 | \n", "1.010079e+00 | \n", "9.827940e+00 | \n", "7.161846e+00 | \n", "1.889552e-01 | \n", "6.023703e-01 | \n", "2.308151e-01 | \n", "2.732653e-01 | \n", "1.857322e-01 | \n", "... | \n", "6.272396e-01 | \n", "1.600321e+00 | \n", "1.627279e-01 | \n", "1.970207e+01 | \n", "3.520885e-01 | \n", "4.288082e-02 | \n", "2.485654e-01 | \n", "4.789894e-01 | \n", "8.255957e-02 | \n", "4.633803e-02 | \n", "
| min | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "... | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "1.018182e-02 | \n", "-0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "
| 25% | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "1.000000e+00 | \n", "0.000000e+00 | \n", "1.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "... | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "1.307692e+00 | \n", "3.238901e+00 | \n", "0.000000e+00 | \n", "7.142857e-02 | \n", "
| 50% | \n", "0.000000e+00 | \n", "1.000000e+00 | \n", "1.000000e+00 | \n", "0.000000e+00 | \n", "2.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "... | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "1.444444e+00 | \n", "3.506891e+00 | \n", "0.000000e+00 | \n", "1.034483e-01 | \n", "
| 75% | \n", "0.000000e+00 | \n", "2.000000e+00 | \n", "2.000000e+00 | \n", "0.000000e+00 | \n", "3.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "... | \n", "0.000000e+00 | \n", "1.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "0.000000e+00 | \n", "1.615385e+00 | \n", "3.796218e+00 | \n", "0.000000e+00 | \n", "1.428571e-01 | \n", "
| max | \n", "1.000000e+00 | \n", "3.000000e+00 | \n", "1.710000e+02 | \n", "2.011000e+03 | \n", "8.198000e+03 | \n", "1.000000e+00 | \n", "1.360000e+02 | \n", "1.000000e+00 | \n", "1.000000e+00 | \n", "1.000000e+00 | \n", "... | \n", "3.800000e+01 | \n", "1.360000e+02 | \n", "1.000000e+00 | \n", "8.367000e+03 | \n", "1.310000e+02 | \n", "1.000000e+00 | \n", "5.000000e+00 | \n", "6.570554e+00 | \n", "9.545455e-01 | \n", "1.000000e+00 | \n", "
8 rows × 26 columns
\n", "Model: \"Model\"\n",
"\n"
],
"text/plain": [
"\u001b[1mModel: \"Model\"\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
"┃ Layer (type) ┃ Output Shape ┃ Param # ┃\n",
"┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
"│ dense (Dense) │ (None, 128) │ 3,456 │\n",
"├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
"│ dense_1 (Dense) │ (None, 64) │ 8,256 │\n",
"├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
"│ dense_2 (Dense) │ (None, 1) │ 65 │\n",
"└─────────────────────────────────┴────────────────────────┴───────────────┘\n",
"\n"
],
"text/plain": [
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
"┃\u001b[1m \u001b[0m\u001b[1mLayer (type) \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mOutput Shape \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m Param #\u001b[0m\u001b[1m \u001b[0m┃\n",
"┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
"│ dense (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m3,456\u001b[0m │\n",
"├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
"│ dense_1 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m8,256\u001b[0m │\n",
"├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
"│ dense_2 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m1\u001b[0m) │ \u001b[38;5;34m65\u001b[0m │\n",
"└─────────────────────────────────┴────────────────────────┴───────────────┘\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"Total params: 11,777 (46.00 KB)\n", "\n" ], "text/plain": [ "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m11,777\u001b[0m (46.00 KB)\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
Trainable params: 11,777 (46.00 KB)\n", "\n" ], "text/plain": [ "\u001b[1m Trainable params: \u001b[0m\u001b[38;5;34m11,777\u001b[0m (46.00 KB)\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
Non-trainable params: 0 (0.00 B)\n", "\n" ], "text/plain": [ "\u001b[1m Non-trainable params: \u001b[0m\u001b[38;5;34m0\u001b[0m (0.00 B)\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "model = build_model(input_dim=X_train_scaled.shape[1])\n", "model.summary()" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m62s\u001b[0m 2ms/step - accuracy: 0.8986 - loss: 0.2663 - val_accuracy: 0.9128 - val_loss: 0.2386\n", "Epoch 2/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m61s\u001b[0m 2ms/step - accuracy: 0.9132 - loss: 0.2369 - val_accuracy: 0.9150 - val_loss: 0.2329\n", "Epoch 3/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m67s\u001b[0m 2ms/step - accuracy: 0.9167 - loss: 0.2298 - val_accuracy: 0.9177 - val_loss: 0.2269\n", "Epoch 4/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m64s\u001b[0m 2ms/step - accuracy: 0.9186 - loss: 0.2263 - val_accuracy: 0.9194 - val_loss: 0.2247\n", "Epoch 5/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m64s\u001b[0m 2ms/step - accuracy: 0.9199 - loss: 0.2236 - val_accuracy: 0.9203 - val_loss: 0.2235\n", "Epoch 6/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m67s\u001b[0m 2ms/step - accuracy: 0.9207 - loss: 0.2222 - val_accuracy: 0.9224 - val_loss: 0.2197\n", "Epoch 7/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m65s\u001b[0m 2ms/step - accuracy: 0.9215 - loss: 0.2203 - val_accuracy: 0.9209 - val_loss: 0.2216\n", "Epoch 8/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m65s\u001b[0m 2ms/step - accuracy: 0.9218 - loss: 0.2199 - val_accuracy: 0.9228 - val_loss: 0.2176\n", "Epoch 9/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m65s\u001b[0m 2ms/step - accuracy: 0.9224 - loss: 0.2185 - val_accuracy: 0.9221 - val_loss: 0.2191\n", "Epoch 10/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m78s\u001b[0m 2ms/step - accuracy: 0.9229 - loss: 0.2178 - val_accuracy: 0.9230 - val_loss: 0.2180\n", "Epoch 11/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m73s\u001b[0m 2ms/step - accuracy: 0.9231 - loss: 0.2173 - val_accuracy: 0.9230 - val_loss: 0.2170\n", "Epoch 12/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m75s\u001b[0m 2ms/step - accuracy: 0.9234 - loss: 0.2169 - val_accuracy: 0.9240 - val_loss: 0.2163\n", "Epoch 13/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m71s\u001b[0m 2ms/step - accuracy: 0.9236 - loss: 0.2163 - val_accuracy: 0.9238 - val_loss: 0.2162\n", "Epoch 14/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m74s\u001b[0m 2ms/step - accuracy: 0.9239 - loss: 0.2158 - val_accuracy: 0.9244 - val_loss: 0.2150\n", "Epoch 15/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m68s\u001b[0m 2ms/step - accuracy: 0.9240 - loss: 0.2154 - val_accuracy: 0.9240 - val_loss: 0.2161\n", "Epoch 16/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m67s\u001b[0m 2ms/step - accuracy: 0.9240 - loss: 0.2156 - val_accuracy: 0.9234 - val_loss: 0.2171\n", "Epoch 17/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m65s\u001b[0m 2ms/step - accuracy: 0.9244 - loss: 0.2146 - val_accuracy: 0.9249 - val_loss: 0.2151\n", "Epoch 18/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m65s\u001b[0m 2ms/step - accuracy: 0.9243 - loss: 0.2148 - val_accuracy: 0.9236 - val_loss: 0.2163\n", "Epoch 19/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m66s\u001b[0m 2ms/step - accuracy: 0.9247 - loss: 0.2137 - val_accuracy: 0.9247 - val_loss: 0.2144\n", "Epoch 20/20\n", "\u001b[1m34976/34976\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m70s\u001b[0m 2ms/step - accuracy: 0.9247 - loss: 0.2139 - val_accuracy: 0.9239 - val_loss: 0.2162\n" ] } ], "source": [ "history = model.fit(X_train_scaled, y_train,\n", " epochs=20, batch_size=128,\n", " validation_split=0.2)\n" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1m43720/43720\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m46s\u001b[0m 1ms/step - accuracy: 0.9243 - loss: 0.2154\n", "Validation Loss: 0.2155\n", "Validation Accuracy: 0.9244\n" ] } ], "source": [ "# 모델 평가 (검증 데이터 기준)\n", "loss, accuracy = model.evaluate(X_test_scaled, y_test)\n", "\n", "# 결과 출력\n", "print(f\"Validation Loss: {loss:.4f}\")\n", "print(f\"Validation Accuracy: {accuracy:.4f}\")" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1m43720/43720\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m28s\u001b[0m 636us/step\n" ] } ], "source": [ "y_test_pred = model.predict(X_test_scaled) # 테스트 데이터에 대해 예측\n", "y_test_pred = (y_test_pred > 0.3549).astype(int) # 이진 분류이므로 0.5를 기준으로 0과 1로 변환\n" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "image/png": "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7MaNG6VLly6ebboEfaVKlWTNmjURHluz+2+//VbWr19vugB2794tCxYskNdeey32rcjXqFEj8wHofP2XXnpJatas6XWD72TLlMqU/Jeu2+bZdv7iVdnwx14pXTibZ1valEnk82715I1u38jlK96XOlab/jogt1y3pGHNxyUoyJGkiRNI/WqlZOm67V4B/7Uaj0v2TKnk4y8ivoJiaPHixpFXqj4m42f/96UPjh/XnFSErkBcvfav3LrlkrJFc9735wC76JU6f1gwX65cuSxFihQL9/hff/4h27f9LS+8WDvcY30+6iXly5WW+nVry8wZ0yKsLtV9qZY8U/4JebNpE9m8aeN9twOx+4I7ffv2Nd3VoW+6LayTJ0+a74IOXA9N72v/fkQ0w+/du7c88cQTJjHOmTOnVKhQQd5///3Yl+lrSWPRokXmzfjizMp166Y4QXF82MLAkT717WwkbH/48VMXJF2q/zKV0b1flS+nrZRNf+2XLBlShjvOvsOn5PmWI+Tb/q/L8K6vSNy4cWTtb7ulVquRnn1yZkkjH7auIZVeHyo3b3pPw4xIjYqFzRiDb+f+V65av3WvXLpyXT5uU1O6D58jjjjyUZua5vXc7wW4k392bJfX6r8i169fM+t/DPlshOTMlSvcfjOnT5McOXJK0WLe64O0bNVaSpV+XBKEhMiaVSulz4e95PLly9Lg1dv9+GnSpJEPevSSAgUKmuxtxvTvTR//t5OnSr78BaLcDsRuXbp0Mf30oYXN8u/X8uXLpU+fPvL555+bMQA7d+40q9l++OGH0q1bt9gV9DNnznzH0ti96FmUjgcILU66xyRehjuPgMTdaX9/koQJZODXP95xn3SptBJQXybOXSdTF26UxImCpftbz8ukT96Qai2Gm+x/fJ/G8tGoBbJz//FIvW6jWmVl0aq/zBiC0OX/Bp3GyGfv1zXt0gxfX09PRm7Rn497yJYtu0ydPksuXrwgi39cJN3e7yxjxn3rFXCvXr0qPyyYJ81atAz3/Dffetvzc758+eXKlSum398d9LNlz2FubnrScPDAAZnwzTjp029glNqB2F/eD46glB8RHXkfJ04cOXbsmNd2vZ8+ffoIn6OBXUv5TZs29VyQTi9W17x5c+natavpHog15f1BgwZJp06dZO/evfd1ZnXu3DmvW9x0JaKlnYHg6MnznvJ9aDrA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", "text/plain": [ "