Первый запуск
This commit is contained in:
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
10
.idea/LogsPatternExtractor.iml
generated
Normal file
10
.idea/LogsPatternExtractor.iml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
12
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
12
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="ignoredErrors">
|
||||
<list>
|
||||
<option value="N802" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.12 (LogsPatternExtractor)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (LogsPatternExtractor)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/LogsPatternExtractor.iml" filepath="$PROJECT_DIR$/.idea/LogsPatternExtractor.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
13
Generator/Enums/RandomType.py
Normal file
13
Generator/Enums/RandomType.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from enum import Enum, auto
|
||||
|
||||
|
||||
class RandomType(Enum):
|
||||
IP = auto()
|
||||
DATE = auto()
|
||||
EMAIL = auto()
|
||||
STATUS_CODE = auto()
|
||||
PATH = auto()
|
||||
USERNAME = auto()
|
||||
INT = auto()
|
||||
VERSION = auto()
|
||||
ID = auto()
|
||||
178
Generator/LogGenerator.py
Normal file
178
Generator/LogGenerator.py
Normal file
@@ -0,0 +1,178 @@
|
||||
import random
|
||||
import re
|
||||
|
||||
from sentence_transformers import InputExample
|
||||
|
||||
from Generator.Enums.RandomType import RandomType
|
||||
from Generator.Models.ConstLiteral import ConstLiteral
|
||||
from Generator.Models.Term import Term
|
||||
from Generator.Models.VariableLiteral import VariableLiteral
|
||||
from Generator.UniversalRandomizer import UniversalRandomizer
|
||||
|
||||
|
||||
class LogGenerator:
|
||||
def __init__(self):
|
||||
# Обертки для переменных: id=..., [ip], 'user'
|
||||
self.wrappers = [("", ""), ("", ""), ("id=", ""), ("user:", ""), ("[", "]"), ("'", "'")]
|
||||
|
||||
# Словарь для констант (имитация логов)
|
||||
self.log_keywords = [
|
||||
# Уровни логирования
|
||||
"INFO", "ERROR", "WARN", "DEBUG", "TRACE", "CRITICAL", "FATAL", "NOTICE",
|
||||
|
||||
# Действия (Verbs)
|
||||
"started", "stopped", "failed", "completed", "aborted", "retrying",
|
||||
"connecting", "disconnected", "listening", "resolving", "binding",
|
||||
"parsing", "rendering", "authenticating", "authorizing", "validated",
|
||||
"rejected", "accepted", "dropped", "created", "deleted", "updated",
|
||||
"fetching", "sending", "receiving", "waiting", "killing", "spawning",
|
||||
|
||||
# Сущности (Nouns)
|
||||
"System", "Kernel", "Thread", "Process", "Worker", "Daemon", "Job",
|
||||
"Connection", "Session", "User", "Client", "Server", "Proxy", "Gateway",
|
||||
"Database", "Table", "Index", "Query", "Transaction", "Commit", "Rollback",
|
||||
"Cache", "Buffer", "Heap", "Stack", "Memory", "Disk", "Volume",
|
||||
"Network", "Port", "Socket", "Interface", "Protocol", "Packet",
|
||||
"Request", "Response", "Header", "Body", "Payload", "Token", "Key",
|
||||
"File", "Directory", "Path", "Config", "Module", "Plugin", "Component",
|
||||
"Exception", "Error", "Timeout", "Latency", "HealthCheck", "Status",
|
||||
|
||||
# HTTP и Web
|
||||
"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD",
|
||||
"HTTP/1.1", "HTTP/2", "API", "Endpoint", "Route", "URI", "URL",
|
||||
"JSON", "XML", "YAML", "HTML", "CSS", "JS",
|
||||
|
||||
# Предлоги и связки
|
||||
"at", "in", "on", "to", "from", "by", "with", "for", "via", "through",
|
||||
|
||||
# Прилагательные и состояния
|
||||
"successful", "failed", "denied", "allowed", "active", "inactive",
|
||||
"pending", "queued", "blocked", "locked", "corrupted", "invalid",
|
||||
"missing", "found", "available", "unavailable", "busy", "idle",
|
||||
"secure", "insecure", "public", "private", "local", "remote"
|
||||
]
|
||||
|
||||
def generate(self, min_literals=15, max_literals=25) -> Term:
|
||||
count = random.randint(min_literals, max_literals)
|
||||
literals = []
|
||||
|
||||
for i in range(count):
|
||||
# 60% Константа, 40% Переменная
|
||||
if random.random() < 0.6:
|
||||
# Либо слово из словаря, либо случайное слово
|
||||
txt = random.choice(self.log_keywords) if random.random() < 0.8 else UniversalRandomizer.fake.text.word()
|
||||
literals.append(ConstLiteral(text=txt))
|
||||
else:
|
||||
r_type = random.choice(list(RandomType))
|
||||
pref, post = random.choice(self.wrappers)
|
||||
literals.append(VariableLiteral(name=f"v{i}", type=r_type, prefix=pref, postfix=post))
|
||||
|
||||
return Term(literals=literals, separator=random.choice([" ", ";", "|"]))
|
||||
|
||||
def generate_training_data(self, count=100):
|
||||
train_examples = []
|
||||
|
||||
for _ in range(count):
|
||||
anchor_term = self.generate()
|
||||
|
||||
anchor_text = anchor_term.render().text
|
||||
|
||||
# 2. Генерируем Positive (Позитивный пример)
|
||||
positive_text = anchor_term.render().text
|
||||
|
||||
# 3. Генерируем Hard Negative
|
||||
literals_copy = anchor_term.literals[:]
|
||||
random.shuffle(literals_copy)
|
||||
|
||||
negative_hard_text = anchor_term.separator.join([lit.render().text for lit in literals_copy])
|
||||
|
||||
# 4. Генерируем Easy Negative (Совсем другой шаблон)
|
||||
random_other_term = self.generate()
|
||||
negative_easy_text = random_other_term.render().text
|
||||
|
||||
# 3. Генерируем Very Hard Negative
|
||||
|
||||
bad_sep = random.choice([" ", ";", "|", " "])
|
||||
negative_very_hard_text = bad_sep.join([lit.render().text for lit in literals_copy])
|
||||
|
||||
# 5. Упаковываем для Sentence Transformers
|
||||
|
||||
# Перемешивание, но с сохранением разделителя
|
||||
train_examples.append(InputExample(texts=[
|
||||
self.mask_log_structure(anchor_text),
|
||||
self.mask_log_structure(positive_text),
|
||||
self.mask_log_structure(negative_hard_text)
|
||||
]))
|
||||
|
||||
# Другой лог
|
||||
train_examples.append(InputExample(texts=[
|
||||
self.mask_log_structure(anchor_text),
|
||||
self.mask_log_structure(positive_text),
|
||||
self.mask_log_structure(negative_easy_text)
|
||||
]))
|
||||
|
||||
# Перемешивание + случайный разделитель
|
||||
train_examples.append(InputExample(texts=[
|
||||
self.mask_log_structure(anchor_text),
|
||||
self.mask_log_structure(positive_text),
|
||||
self.mask_log_structure(negative_very_hard_text)
|
||||
]))
|
||||
|
||||
return train_examples
|
||||
|
||||
def mask_log_structure(self, text: str) -> str:
|
||||
# 1. GUID / UUID (строгий паттерн)
|
||||
# Пример: 123e4567-e89b-12d3-a456-426614174000
|
||||
text = re.sub(r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}', '<GUID>', text)
|
||||
|
||||
# 2. IP-адреса (IPv4)
|
||||
# Пример: 192.168.0.1
|
||||
# Важно делать ДО флоатов, иначе 192.168 определится как Float
|
||||
text = re.sub(r'\d{1,3}(?:\.\d{1,3}){3}', '<IP>', text)
|
||||
|
||||
# 3. Числа с плавающей точкой (Floats)
|
||||
# Пример: 0.05, 123.45, -3.14
|
||||
# (?<!\.) - проверка, что перед числом нет точки (чтобы не ломать IP, если вдруг проскочил)
|
||||
text = re.sub(r'-?\d+\.\d+', '<NUM>', text)
|
||||
|
||||
# 4. Целые числа (Integers)
|
||||
# Пример: 404, 500, -1
|
||||
text = re.sub(r'-?\d+', '<NUM>', text)
|
||||
|
||||
# 5. (Опционально) Hex-строки (адреса памяти, хеши)
|
||||
# Пример: 0x7fff5fbff
|
||||
text = re.sub(r'0x[0-9a-fA-F]+', '<HEX>', text)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
gen = LogGenerator()
|
||||
gen.generate_training_data(count=1)
|
||||
|
||||
print("Пример генерации датасета:\n")
|
||||
|
||||
# Генерируем 5 примеров
|
||||
for i in range(10):
|
||||
# 1. Получаем объект Term
|
||||
term = gen.generate()
|
||||
|
||||
# 3. Используем данные (например, сохраняем в JSON для обучения)
|
||||
print(f"--- Sample {i + 1} ---")
|
||||
result = term.render()
|
||||
print(f"{term.structure().text}")
|
||||
|
||||
for j in range(5):
|
||||
# 2. Рендерим его в строку и метаданные
|
||||
result = term.render()
|
||||
|
||||
print(f"Positive {j}: {result.text}")
|
||||
|
||||
for j in range(5):
|
||||
# 2. Рендерим его в строку и метаданные
|
||||
random.shuffle(term.literals)
|
||||
term.separator = random.choice([" ", ";", "|"])
|
||||
result = term.render()
|
||||
|
||||
print(f"Negative {j}: {result.text}")
|
||||
15
Generator/Models/ConstLiteral.py
Normal file
15
Generator/Models/ConstLiteral.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from Generator.Models.Literal import Literal
|
||||
from Generator.Models.RenderResult import RenderResult
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConstLiteral(Literal):
|
||||
text: str
|
||||
|
||||
def render(self, chanse: float = 1) -> RenderResult:
|
||||
return RenderResult(self.text, [])
|
||||
|
||||
def structure(self) -> RenderResult:
|
||||
return self.render()
|
||||
12
Generator/Models/Literal.py
Normal file
12
Generator/Models/Literal.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from Generator.Models.RenderResult import RenderResult
|
||||
|
||||
|
||||
@dataclass
|
||||
class Literal:
|
||||
def render(self, chanse: float = 1) -> RenderResult:
|
||||
return RenderResult("", [])
|
||||
|
||||
def structure(self) -> RenderResult:
|
||||
return RenderResult("", [])
|
||||
8
Generator/Models/RenderResult.py
Normal file
8
Generator/Models/RenderResult.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Tuple
|
||||
|
||||
|
||||
@dataclass
|
||||
class RenderResult:
|
||||
text: str
|
||||
spans: List[Tuple[int, int, str]]
|
||||
53
Generator/Models/Term.py
Normal file
53
Generator/Models/Term.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
from Generator.Models.ConstLiteral import ConstLiteral
|
||||
from Generator.Models.Literal import Literal
|
||||
from Generator.Models.RenderResult import RenderResult
|
||||
from Generator.Models.VariableLiteral import VariableLiteral
|
||||
|
||||
|
||||
@dataclass
|
||||
class Term:
|
||||
literals: List[Literal]
|
||||
separator: str = " "
|
||||
|
||||
def render(self, chanse: float = 1) -> RenderResult:
|
||||
final_text = ""
|
||||
final_spans = []
|
||||
|
||||
for i, literal in enumerate(self.literals):
|
||||
res = literal.render(chanse)
|
||||
|
||||
current_offset = len(final_text)
|
||||
final_text += res.text
|
||||
|
||||
# Сдвигаем координаты с учетом позиции слова в строке
|
||||
for (start, end, label) in res.spans:
|
||||
final_spans.append((current_offset + start, current_offset + end, label))
|
||||
|
||||
# Добавляем разделитель, если это не последнее слово
|
||||
if i < len(self.literals) - 1:
|
||||
final_text += self.separator
|
||||
|
||||
return RenderResult(final_text, final_spans)
|
||||
|
||||
def structure(self) -> RenderResult:
|
||||
final_text = ""
|
||||
final_spans = []
|
||||
|
||||
for i, literal in enumerate(self.literals):
|
||||
res = literal.structure()
|
||||
|
||||
current_offset = len(final_text)
|
||||
final_text += res.text
|
||||
|
||||
# Сдвигаем координаты с учетом позиции слова в строке
|
||||
for (start, end, label) in res.spans:
|
||||
final_spans.append((current_offset + start, current_offset + end, label))
|
||||
|
||||
# Добавляем разделитель, если это не последнее слово
|
||||
if i < len(self.literals) - 1:
|
||||
final_text += self.separator
|
||||
|
||||
return RenderResult(final_text, final_spans)
|
||||
45
Generator/Models/VariableLiteral.py
Normal file
45
Generator/Models/VariableLiteral.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import random
|
||||
from dataclasses import dataclass
|
||||
|
||||
from Generator.Enums.RandomType import RandomType
|
||||
from Generator.Models.Literal import Literal
|
||||
from Generator.Models.RenderResult import RenderResult
|
||||
from Generator.UniversalRandomizer import UniversalRandomizer
|
||||
|
||||
|
||||
@dataclass
|
||||
class VariableLiteral(Literal):
|
||||
name: str
|
||||
type: RandomType
|
||||
prefix: str = ""
|
||||
postfix: str = ""
|
||||
last_value: str | None = None
|
||||
|
||||
def render(self, chanse: float = 1) -> RenderResult:
|
||||
if self.last_value is None or random.random() <= chanse:
|
||||
# Генерируем значение
|
||||
val = str(UniversalRandomizer().get_random(self.type))
|
||||
self.last_value = val
|
||||
else:
|
||||
val = self.last_value
|
||||
|
||||
# Формируем строку: префикс + значение + постфикс
|
||||
full_text = f"{self.prefix}{val}{self.postfix}"
|
||||
|
||||
# Вычисляем координаты ЧИСТОГО значения (без префикса)
|
||||
start = len(self.prefix)
|
||||
end = start + len(val)
|
||||
|
||||
return RenderResult(full_text, [(start, end, self.type.name)])
|
||||
|
||||
def structure(self) -> RenderResult:
|
||||
val = f"<{self.type.name}>"
|
||||
|
||||
# Формируем строку: префикс + значение + постфикс
|
||||
full_text = f"{self.prefix}{val}{self.postfix}"
|
||||
|
||||
# Вычисляем координаты ЧИСТОГО значения (без префикса)
|
||||
start = len(self.prefix)
|
||||
end = start + len(val)
|
||||
|
||||
return RenderResult(full_text, [(start, end, self.type.name)])
|
||||
31
Generator/UniversalRandomizer.py
Normal file
31
Generator/UniversalRandomizer.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import random
|
||||
from typing import Any
|
||||
|
||||
from Generator.Enums.RandomType import RandomType
|
||||
from mimesis import Generic
|
||||
from mimesis.locales import Locale
|
||||
|
||||
|
||||
class UniversalRandomizer:
|
||||
fake = Generic(locale=Locale.EN)
|
||||
|
||||
def get_random(self, r_type: RandomType) -> Any:
|
||||
if r_type == RandomType.IP:
|
||||
return self.fake.internet.ip_v4()
|
||||
if r_type == RandomType.DATE:
|
||||
return self.fake.datetime.date().isoformat()
|
||||
if r_type == RandomType.EMAIL:
|
||||
return self.fake.person.email()
|
||||
if r_type == RandomType.STATUS_CODE:
|
||||
return self.fake.internet.http_status_code()
|
||||
if r_type == RandomType.PATH:
|
||||
return f"/var/log/{self.fake.file.file_name()}"
|
||||
if r_type == RandomType.USERNAME:
|
||||
return self.fake.person.username()
|
||||
if r_type == RandomType.INT:
|
||||
return random.randint(1, 9999)
|
||||
if r_type == RandomType.VERSION:
|
||||
return self.fake.development.version()
|
||||
if r_type == RandomType.ID:
|
||||
return self.fake.cryptographic.uuid().split('-')[0]
|
||||
return "UNKNOWN"
|
||||
36
Infrostructure/ProtocolCoder/BitReader.py
Normal file
36
Infrostructure/ProtocolCoder/BitReader.py
Normal file
@@ -0,0 +1,36 @@
|
||||
class BitReader:
|
||||
"""
|
||||
Класс для чтение битов из байтовой строки (bytes).
|
||||
"""
|
||||
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.bit_pos = 0
|
||||
self.total_bits = len(data) * 8
|
||||
|
||||
def read_bits(self, length):
|
||||
"""
|
||||
Считывает length бит и возвращает их как целое число.
|
||||
"""
|
||||
if self.bit_pos + length > self.total_bits:
|
||||
raise ValueError(f"Недостаточно данных: запрошено {length}, осталось {self.remaining()}")
|
||||
|
||||
value = 0
|
||||
# Читаем побитово (можно оптимизировать, но так надежнее для понимания)
|
||||
for _ in range(length):
|
||||
byte_index = self.bit_pos // 8
|
||||
# В байте биты идут слева направо (7..0), где 7 - старший
|
||||
bit_offset = 7 - (self.bit_pos % 8)
|
||||
|
||||
bit = (self.data[byte_index] >> bit_offset) & 1
|
||||
value = (value << 1) | bit
|
||||
|
||||
self.bit_pos += 1
|
||||
return value
|
||||
|
||||
def has_bits(self, length):
|
||||
"""Проверяет, осталось ли достаточно бит для чтения."""
|
||||
return self.bit_pos + length <= self.total_bits
|
||||
|
||||
def remaining(self):
|
||||
return self.total_bits - self.bit_pos
|
||||
34
Infrostructure/ProtocolCoder/BitWriter.py
Normal file
34
Infrostructure/ProtocolCoder/BitWriter.py
Normal file
@@ -0,0 +1,34 @@
|
||||
class BitWriter:
|
||||
"""
|
||||
Класс для накопления бит и их конвертации в байтовую строку.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.value = 0
|
||||
self.bit_count = 0
|
||||
|
||||
def add_bits(self, val, length):
|
||||
"""
|
||||
Добавляет length бит из числа val в поток.
|
||||
"""
|
||||
# Сдвигаем текущее накопленное значение влево на length
|
||||
self.value = (self.value << length) | (val & ((1 << length) - 1))
|
||||
self.bit_count += length
|
||||
|
||||
def get_bytes(self):
|
||||
"""
|
||||
Возвращает накопленные биты в виде объекта bytes.
|
||||
Если количество бит не кратно 8, дополняет нулями справа (до полного байта).
|
||||
"""
|
||||
if self.bit_count == 0:
|
||||
return b''
|
||||
|
||||
# Вычисляем количество необходимых байт
|
||||
num_bytes = (self.bit_count + 7) // 8
|
||||
|
||||
# Сдвигаем значение влево, чтобы заполнить последний байт, если он не полон
|
||||
# Например, если есть 4 бита 1010, нам нужно получить байт 10100000 (0xA0)
|
||||
shift_remainder = (num_bytes * 8) - self.bit_count
|
||||
final_value = self.value << shift_remainder
|
||||
|
||||
return final_value.to_bytes(num_bytes, byteorder='big')
|
||||
218
Infrostructure/ProtocolCoder/MessageEncoder.py
Normal file
218
Infrostructure/ProtocolCoder/MessageEncoder.py
Normal file
@@ -0,0 +1,218 @@
|
||||
import time
|
||||
|
||||
from Infrostructure.ProtocolCoder.BitReader import BitReader
|
||||
from Infrostructure.ProtocolCoder.BitWriter import BitWriter
|
||||
|
||||
|
||||
class MessageEncoder:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def encode_protocol(self, template_id, variables, section_power=3):
|
||||
|
||||
# --- 1. Секция заголовков ---
|
||||
writer = BitWriter()
|
||||
|
||||
# Поле 1: Размер секции (1 байт)
|
||||
# Здесь указываем саму степень (например, 3)
|
||||
writer.add_bits(section_power, 8)
|
||||
|
||||
# Вычисляем размер одной секции в битах (S)
|
||||
section_size_bits = 1 << section_power
|
||||
# Максимальное число, которое можно записать в поле, описывающее длину (например, для 8 бит это 255)
|
||||
max_len_per_section = (1 << section_size_bits) - 1
|
||||
|
||||
# Поле 2: Зарезервированная область (4 секции)
|
||||
# 4 секции * section_size_bits
|
||||
writer.add_bits(0, 4 * section_size_bits)
|
||||
|
||||
# --- 2. Секция шаблона ---
|
||||
|
||||
# Определяем битовую длину ID шаблона
|
||||
# Если ID=0, нужно хотя бы 1 бит, но bit_length() вернет 0, обрабатываем это
|
||||
tn = template_id.bit_length() if template_id > 0 else 1
|
||||
|
||||
# Поле 3: Размер следующей секции (tn) в секциях (размер поля = 1 секция)
|
||||
# Внимание: в ТЗ написано "1 секция – размер следующей секции ... в битах".
|
||||
writer.add_bits(tn, section_size_bits)
|
||||
|
||||
# Поле 4: Идентификатор шаблона (tn бит)
|
||||
writer.add_bits(template_id, tn)
|
||||
|
||||
# --- 3. Секции данных ---
|
||||
|
||||
for var_id, var_val in variables:
|
||||
# Подготовка значения переменной
|
||||
if isinstance(var_val, str):
|
||||
# Если строка, берем код первого символа (для примера 'A' -> 65)
|
||||
# Для полноценных строк нужно кодировать в байты, здесь упрощение под "числовые переменные"
|
||||
if len(var_val) == 1:
|
||||
val_int = ord(var_val)
|
||||
else:
|
||||
# Если пришла длинная строка, кодируем как большое число
|
||||
val_bytes = var_val.encode('utf-8')
|
||||
val_int = int.from_bytes(val_bytes, byteorder='big')
|
||||
else:
|
||||
val_int = var_val
|
||||
|
||||
# Определяем необходимые биты для значения и ID
|
||||
# Используем bit_length для максимальной компактности
|
||||
# Однако, в примере ID=1 (1 бит) записан в 4 бита.
|
||||
# Алгоритм: берем минимально необходимый размер, либо выравниваем, если требуется.
|
||||
# ТЗ: "вписываются в максимально компактном виде". Значит, берем реальный bit_length.
|
||||
|
||||
# Биты для значения
|
||||
val_total_bits = val_int.bit_length() if val_int > 0 else 1
|
||||
# Биты для ID
|
||||
id_bits = var_id.bit_length() if var_id > 0 else 1
|
||||
|
||||
# Логика разбиения на секции, если значение не влезает в одну секцию описания размера.
|
||||
# Поле размера (xn) само имеет размер 1 секцию (например, 8 бит).
|
||||
# Значит, максимальная длина блока данных = 255 бит.
|
||||
# Если val_total_bits > 255, нужно разбивать на несколько секций данных.
|
||||
|
||||
bits_left = val_total_bits
|
||||
|
||||
# Для корректной нарезки битов большого числа нам удобно преобразовать его в строку или срезать маской
|
||||
# Но проще математически брать куски от старших бит к младшим или наоборот.
|
||||
# Порядок записи битов: обычно Big Endian.
|
||||
|
||||
while bits_left > 0:
|
||||
# Определяем, сколько бит значения запишем в этот блок
|
||||
# Либо всё что осталось, либо максимум, который можно описать одним числом в поле размера
|
||||
chunk_size = min(bits_left, max_len_per_section)
|
||||
|
||||
# Вырезаем нужный кусок (chunk) из числа val_int
|
||||
# Нам нужны старшие биты из оставшихся.
|
||||
# Пример: всего 10 бит, берем 8. Нужно сдвинуть (10-8)=2 раза вправо.
|
||||
shift = bits_left - chunk_size
|
||||
chunk_val = (val_int >> shift) & ((1 << chunk_size) - 1)
|
||||
|
||||
# Поле 5: Размер ID в битах (n) - занимает 1 секцию
|
||||
writer.add_bits(id_bits, section_size_bits)
|
||||
|
||||
# Поле 6: Размер блока значения в битах (xn) - занимает 1 секцию
|
||||
writer.add_bits(chunk_size, section_size_bits)
|
||||
|
||||
# Поле 7: Идентификатор (n бит)
|
||||
writer.add_bits(var_id, id_bits)
|
||||
|
||||
# Поле 8: Блок значения (xn бит)
|
||||
writer.add_bits(chunk_val, chunk_size)
|
||||
|
||||
bits_left -= chunk_size
|
||||
|
||||
return writer.get_bytes()
|
||||
|
||||
def decode_protocol(self, data):
|
||||
"""
|
||||
Декодирует бинарные данные обратно в ID шаблона и список переменных.
|
||||
|
||||
:param data: bytes объект
|
||||
:return: кортеж (template_id, list_of_variables)
|
||||
где list_of_variables это список кортежей (var_id, value)
|
||||
"""
|
||||
reader = BitReader(data)
|
||||
|
||||
# --- 1. Секция заголовков ---
|
||||
if not reader.has_bits(8):
|
||||
raise ValueError("Пустые данные или некорректный заголовок")
|
||||
|
||||
# 1. Размер секции (степень двойки)
|
||||
section_power = reader.read_bits(8)
|
||||
section_size = 1 << section_power # 2^power
|
||||
|
||||
# 2. Пропускаем зарезервированную область (4 секции)
|
||||
reader.read_bits(4 * section_size)
|
||||
|
||||
# --- 2. Секция шаблона ---
|
||||
|
||||
# 3. Размер ID шаблона (1 секция)
|
||||
tn = reader.read_bits(section_size)
|
||||
|
||||
# 4. Идентификатор шаблона (tn бит)
|
||||
template_id = reader.read_bits(tn)
|
||||
|
||||
# --- 3. Секции данных ---
|
||||
|
||||
variables = []
|
||||
last_var_id = None
|
||||
|
||||
# Читаем, пока есть данные.
|
||||
# Минимальный блок данных требует 2 секции заголовков (размер ID и размер значения)
|
||||
while reader.has_bits(2 * section_size):
|
||||
# 5. Размер ID переменной (1 секция)
|
||||
n = reader.read_bits(section_size)
|
||||
|
||||
# 6. Размер значения переменной (1 секция)
|
||||
xn = reader.read_bits(section_size)
|
||||
|
||||
# Проверяем, хватает ли бит на само тело данных
|
||||
# (Это может случиться, если в конце файла "мусорные" нули для выравнивания байта)
|
||||
if not reader.has_bits(n + xn):
|
||||
break
|
||||
|
||||
# 7. Идентификатор переменной
|
||||
var_id = reader.read_bits(n)
|
||||
|
||||
# 8. Значение переменной (часть значения)
|
||||
chunk_value = reader.read_bits(xn)
|
||||
|
||||
# Логика склеивания (Reassembly):
|
||||
# Если ID текущей переменной совпадает с ID последней добавленной,
|
||||
# значит это продолжение большого числа, которое было разбито на секции.
|
||||
# Энкодер писал старшие части первыми (Big Endian logic в чанках),
|
||||
# поэтому мы сдвигаем старое значение и добавляем новый кусок.
|
||||
if last_var_id is not None and var_id == last_var_id:
|
||||
# Получаем предыдущее значение
|
||||
_, prev_val = variables.pop()
|
||||
# Сдвигаем его влево на размер нового куска и добавляем новый кусок
|
||||
new_val = (prev_val << xn) | chunk_value
|
||||
variables.append((var_id, new_val))
|
||||
else:
|
||||
# Новая переменная
|
||||
variables.append((var_id, chunk_value))
|
||||
last_var_id = var_id
|
||||
|
||||
return template_id, variables
|
||||
|
||||
def get_hex(self, data):
|
||||
return " ".join(f"{b:02X}" for b in data)
|
||||
|
||||
def from_hex(self, hex_str):
|
||||
return bytes.fromhex(hex_str)
|
||||
|
||||
def int_to_str(self, number):
|
||||
if number == 0:
|
||||
return ""
|
||||
# 1. Вычисляем, сколько байт занимает число
|
||||
# (bit_length() + 7) // 8 — это округление вверх до целого байта
|
||||
num_bytes = (number.bit_length() + 7) // 8
|
||||
|
||||
# 2. Превращаем число в байты
|
||||
# Важно использовать byteorder='big', так как энкодер записывал старшие байты первыми
|
||||
bytes_data = number.to_bytes(num_bytes, byteorder='big')
|
||||
|
||||
# 3. Декодируем байты в строку
|
||||
try:
|
||||
return bytes_data.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
# Если число не является валидной utf-8 строкой, возвращаем как есть или hex
|
||||
return f"<Binary: {bytes_data.hex()}>"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
me = MessageEncoder()
|
||||
hex = "03 00 00 00 00 01 81 27 59 18 19 1A 96 98 19 16 98 19 00 8F F9 37 B7 BA 00"
|
||||
|
||||
# Генерируем
|
||||
binary_data = me.from_hex(hex)
|
||||
|
||||
t = time.time()
|
||||
for i in range(1000):
|
||||
data = me.decode_protocol(binary_data)
|
||||
print((time.time() - t )*1000)
|
||||
|
||||
tmp = [(i[0], me.int_to_str(i[1])) if i[1] > 100000 else i for i in data[1]]
|
||||
|
||||
print(data[0], tmp)
|
||||
98
Infrostructure/RabbitMQ/RabbitMQMessenger.py
Normal file
98
Infrostructure/RabbitMQ/RabbitMQMessenger.py
Normal file
@@ -0,0 +1,98 @@
|
||||
import pika
|
||||
import sys
|
||||
|
||||
|
||||
class RabbitMQMessenger:
|
||||
def __init__(self, host='k8s.worker', username='rabbit', password='rabbit', port=32294):
|
||||
"""
|
||||
Инициализация подключения к RabbitMQ.
|
||||
"""
|
||||
self.credentials = pika.PlainCredentials(username, password)
|
||||
self.parameters = pika.ConnectionParameters(
|
||||
host=host,
|
||||
port=port,
|
||||
credentials=self.credentials,
|
||||
# heartbeat нужен, чтобы соединение не рвалось при долгом ожидании
|
||||
heartbeat=600
|
||||
)
|
||||
self.connection = None
|
||||
self.channel = None
|
||||
self._connect()
|
||||
|
||||
def _connect(self):
|
||||
"""Создаем соединение и канал."""
|
||||
try:
|
||||
self.connection = pika.BlockingConnection(self.parameters)
|
||||
self.channel = self.connection.channel()
|
||||
except Exception as e:
|
||||
print(f"Ошибка подключения к RabbitMQ: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
def send_message(self, queue_name: str, message: str):
|
||||
"""
|
||||
Отправка сообщения в очередь.
|
||||
:param queue_name: Имя очереди, куда отправляем данные.
|
||||
:param message: Данные (текст).
|
||||
"""
|
||||
# Объявляем очередь (durable=True значит, что очередь переживет перезагрузку RabbitMQ)
|
||||
self.channel.queue_declare(queue=queue_name, durable=True)
|
||||
|
||||
self.channel.basic_publish(
|
||||
exchange='',
|
||||
routing_key=queue_name,
|
||||
body=message.encode('utf-8'), # Превращаем строку в байты
|
||||
properties=pika.BasicProperties(
|
||||
delivery_mode=2, # Сделать сообщение персистентным (сохранить на диске)
|
||||
))
|
||||
print(f"[x] Отправлено в '{queue_name}': {message}")
|
||||
|
||||
def send_binary_message(self, queue_name: str,message: bytes):
|
||||
# Объявляем очередь (durable=True значит, что очередь переживет перезагрузку RabbitMQ)
|
||||
self.channel.queue_declare(queue=queue_name, durable=True)
|
||||
|
||||
self.channel.basic_publish(
|
||||
exchange='',
|
||||
routing_key=queue_name,
|
||||
body=message, # Превращаем строку в байты
|
||||
properties=pika.BasicProperties(
|
||||
delivery_mode=2, # Сделать сообщение персистентным (сохранить на диске)
|
||||
))
|
||||
print(f"[x] Отправлено в '{queue_name}': {message}")
|
||||
|
||||
def start_listening(self, queue_name: str, callback_function):
|
||||
"""
|
||||
Запуск прослушивания очереди (блокирует выполнение скрипта).
|
||||
:param queue_name: Имя очереди, которую слушаем (ответы).
|
||||
:param callback_function: Функция, которая будет вызвана при получении сообщения.
|
||||
Должна принимать один аргумент (текст сообщения).
|
||||
"""
|
||||
self.channel.queue_declare(queue=queue_name, durable=True)
|
||||
|
||||
# prefetch_count=1 говорит RabbitMQ не давать работнику больше 1 сообщения за раз,
|
||||
# пока он не обработает предыдущее.
|
||||
self.channel.basic_qos(prefetch_count=1)
|
||||
|
||||
# Внутренняя обертка, чтобы декодировать байты в текст перед передачей в ваш callback
|
||||
def internal_callback(ch, method, properties, body):
|
||||
text_data = body.decode('utf-8')
|
||||
print(f"[v] Получено из '{queue_name}'")
|
||||
|
||||
# Вызываем вашу логику обработки
|
||||
callback_function(text_data)
|
||||
|
||||
# Подтверждаем выполнение (ACK), чтобы сообщение удалилось из очереди
|
||||
ch.basic_ack(delivery_tag=method.delivery_tag)
|
||||
|
||||
self.channel.basic_consume(queue=queue_name, on_message_callback=internal_callback)
|
||||
|
||||
print(f"[*] Ожидание сообщений в очереди '{queue_name}'. Нажмите CTRL+C для выхода.")
|
||||
try:
|
||||
self.channel.start_consuming()
|
||||
except KeyboardInterrupt:
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
"""Закрытие соединения."""
|
||||
if self.connection and not self.connection.is_closed:
|
||||
self.connection.close()
|
||||
print("\n[!] Соединение закрыто")
|
||||
116
LogProcessingWorker.py
Normal file
116
LogProcessingWorker.py
Normal file
@@ -0,0 +1,116 @@
|
||||
import os
|
||||
|
||||
from Infrostructure.ProtocolCoder.MessageEncoder import MessageEncoder
|
||||
from Infrostructure.RabbitMQ.RabbitMQMessenger import RabbitMQMessenger
|
||||
from Processor.StreamingLogCluster import StreamingLogCluster
|
||||
|
||||
|
||||
class LogProcessingWorker:
|
||||
def __init__(self,
|
||||
model_path: str,
|
||||
db_path: str,
|
||||
input_queue: str = 'logs_input',
|
||||
output_queue: str = 'logs_output',
|
||||
output_debug_queue: str = 'logs_debug_output',):
|
||||
|
||||
if os.path.exists(db_path):
|
||||
os.remove(db_path)
|
||||
|
||||
self.output_queue = output_queue
|
||||
self.output_debug_queue = output_debug_queue
|
||||
|
||||
print("--- ЗАПУСК основоного алгоритма ---")
|
||||
self.clusterer = StreamingLogCluster(model_path, db_path=db_path)
|
||||
|
||||
print("--- ЗАПУСК системы кодирования ---")
|
||||
self.encoder = MessageEncoder()
|
||||
|
||||
print("--- ЗАПУСК системы приёма/отправки сообщений ---")
|
||||
self.messenger = RabbitMQMessenger()
|
||||
|
||||
print("--- ЗАПУСК системы чтения сообщений ---")
|
||||
self.messenger.start_listening(
|
||||
queue_name=input_queue,
|
||||
callback_function=self._process_log_callback
|
||||
)
|
||||
|
||||
def _process_log_callback(self, log_text: str):
|
||||
try:
|
||||
log_text = log_text.strip()
|
||||
if not log_text:
|
||||
return
|
||||
|
||||
print(f" [>] Обработка: {log_text[:50]}...")
|
||||
|
||||
# А. Кластеризация
|
||||
# process() возвращает dict, который полностью готов к JSON
|
||||
analysis_result = self.clusterer.process(log_text)
|
||||
|
||||
me = MessageEncoder()
|
||||
|
||||
data = me.encode_protocol(analysis_result['template_id'],
|
||||
[(i['uid'], i['value']) for i in analysis_result['variables']]
|
||||
)
|
||||
|
||||
# Г. Отправка результата в Output очередь
|
||||
# Messenger сам переподключится, если связь мигнула
|
||||
self.messenger.send_binary_message(self.output_queue, data )
|
||||
self.messenger.send_message(self.output_debug_queue, str(analysis_result))
|
||||
|
||||
except Exception as e:
|
||||
print(f" [!] Ошибка внутри логики обработки: {e}")
|
||||
|
||||
|
||||
def local_test():
|
||||
MODEL_PATH = './Resources/model'
|
||||
DB_FILE = "logs.db"
|
||||
TEST_FILE = "./Resources/test/container-qfdpbp.log"
|
||||
|
||||
if os.path.exists(DB_FILE):
|
||||
os.remove(DB_FILE)
|
||||
|
||||
print("--- ЗАПУСК основоного алгоритма ---")
|
||||
clusterer = StreamingLogCluster(MODEL_PATH, db_path=DB_FILE)
|
||||
|
||||
print("--- ЗАПУСК системы кодирования ---")
|
||||
encoder = MessageEncoder()
|
||||
|
||||
me = MessageEncoder()
|
||||
|
||||
new_len = 0
|
||||
|
||||
dict = {}
|
||||
|
||||
with open(TEST_FILE, 'r', errors='ignore') as f:
|
||||
while True:
|
||||
log_text = f.readline()
|
||||
|
||||
if log_text == "":
|
||||
break
|
||||
analysis_result = clusterer.process(log_text)
|
||||
|
||||
data = me.encode_protocol(analysis_result['template_id'],
|
||||
[(i['uid'], i['value']) for i in analysis_result['variables']]
|
||||
)
|
||||
new_len += len(data)
|
||||
|
||||
if analysis_result['template_id'] in dict:
|
||||
dict[analysis_result['template_id']] +=1
|
||||
else:
|
||||
dict[analysis_result['template_id']] = 1
|
||||
print(f"[{len(data)}]->({analysis_result['template_id']})",data)
|
||||
|
||||
print(new_len / 1024)
|
||||
print(dict,sep="\n")
|
||||
|
||||
if __name__ == '__main__':
|
||||
local_test()
|
||||
# MODEL_PATH = './Resources/model'
|
||||
# DB_FILE = "logs.db"
|
||||
# INPUT_QUEUE = "input"
|
||||
# OUTPUT_QUEUE = "output"
|
||||
# OUTPUT_DEBUG_QUEUE = "debug_output"
|
||||
#
|
||||
# processor = LogProcessingWorker(MODEL_PATH, DB_FILE, INPUT_QUEUE, OUTPUT_QUEUE, OUTPUT_DEBUG_QUEUE)
|
||||
|
||||
|
||||
24
Processor/Models/LogTemplate.py
Normal file
24
Processor/Models/LogTemplate.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from typing import List, Union
|
||||
|
||||
from Processor.Models.LogVariable import LogVariable
|
||||
|
||||
|
||||
class LogTemplate:
|
||||
def __init__(self, uid: int, tokens: List[Union[str, LogVariable]], representative_log: str):
|
||||
self.uid = uid
|
||||
self.tokens = tokens
|
||||
self.representative_log = representative_log
|
||||
self.embedding = None
|
||||
self.hits = 1
|
||||
self.local_var_counter = 1
|
||||
|
||||
def get_tokens_as_str_list(self) -> List[str]:
|
||||
return [str(t) if isinstance(t, LogVariable) else t for t in self.tokens]
|
||||
|
||||
def render(self) -> str:
|
||||
return "".join(str(t) for t in self.tokens)
|
||||
|
||||
def get_next_var_id(self) -> int:
|
||||
vid = self.local_var_counter
|
||||
self.local_var_counter += 1
|
||||
return vid
|
||||
12
Processor/Models/LogVariable.py
Normal file
12
Processor/Models/LogVariable.py
Normal file
@@ -0,0 +1,12 @@
|
||||
class LogVariable:
|
||||
def __init__(self, uid: int, initial_value: str = "", var_type: str = "VAR"):
|
||||
self.uid = uid
|
||||
self.initial_value = initial_value
|
||||
self.var_type = var_type
|
||||
|
||||
def __str__(self):
|
||||
return f"<{self.var_type}_{self.uid}>"
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
417
Processor/StreamingLogCluster.py
Normal file
417
Processor/StreamingLogCluster.py
Normal file
@@ -0,0 +1,417 @@
|
||||
import difflib
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from typing import List, Dict, Any, Union, Optional
|
||||
|
||||
import numpy as np
|
||||
from sentence_transformers import SentenceTransformer, util
|
||||
|
||||
from Processor.Models.LogTemplate import LogTemplate
|
||||
from Processor.Models.LogVariable import LogVariable
|
||||
from Processor.TemplateDatabase import TemplateDatabase
|
||||
|
||||
|
||||
class StreamingLogCluster:
|
||||
# --- Константы класса для удобства настройки ---
|
||||
THRESHOLD_CREATE_NEW = 0.7 #0.70
|
||||
SCORE_EXACT_MATCH = 0.85
|
||||
SCORE_PARTIAL_MATCH = 0.6
|
||||
MAX_VAR_LEN = 32
|
||||
|
||||
HARD_DELIMITERS = {'|', ';', ','}
|
||||
SOFT_DELIMITERS = {'=', ':', '-', '>', '<', '[', ']', '(', ')', '{', '}', '"', "'"}
|
||||
|
||||
def __init__(self, model_path: str, db_path: str = "logs_knowledge.db"):
|
||||
self.model = SentenceTransformer(model_path)
|
||||
self.db = TemplateDatabase(db_path)
|
||||
|
||||
# Компилируем регулярные выражения один раз
|
||||
self.mask_regex = {
|
||||
'guid': re.compile(r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-...'),
|
||||
'ip': re.compile(r'\d{1,3}(?:\.\d{1,3}){3}'),
|
||||
'ver': re.compile(r'\d{1,3}(?:\.\d{1,3}){2}'),
|
||||
'num': re.compile(r'-?\d+(\.\d+)?'),
|
||||
'base64': re.compile(r'(?<![A-Za-z0-9+/])(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?(?![A-Za-z0-9+/])')
|
||||
}
|
||||
|
||||
token_patterns = [
|
||||
r'(?P<DATE>\d{4}-\d{2}-\d{2}|\d{2}\.\d{2}\.\d{4}|\d{2}/\d{2}/\d{4})',
|
||||
r'(?P<TIME>\d{2}:\d{2}:\d{2}(?:\.\d+)?)',
|
||||
r'(?P<EMAIL>[\w\.-]+@[\w\.-]+\.\w+)',
|
||||
r'(?P<IP>\d{1,3}(?:\.\d{1,3}){3})',
|
||||
r'(?P<VER>\d{1,3}(?:\.\d{1,3}){2})',
|
||||
r'(?P<GUID>[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-...)',
|
||||
r'(?P<WORD>[a-zA-Z0-9_]+)',
|
||||
r'(?P<SYMBOL>[^\w\s])',
|
||||
r'(?P<SPACE>\s+)'
|
||||
]
|
||||
self.master_regex = re.compile('|'.join(token_patterns))
|
||||
self.var_type_names = {'DATE', 'TIME', 'EMAIL', 'IP', 'GUID', "VER"}
|
||||
|
||||
# --- Легковесный индекс в ОЗУ ---
|
||||
self.template_ids: List[int] = []
|
||||
self.embeddings: Optional[np.ndarray] = None
|
||||
self.template_id_counter = self.db.get_max_id() + 1
|
||||
|
||||
self._load_index()
|
||||
|
||||
def _load_index(self):
|
||||
"""Загружает ТОЛЬКО векторы и ID из БД, экономя оперативную память."""
|
||||
print("📥 Загрузка векторного индекса из БД...")
|
||||
|
||||
# Принимаем в одну переменную (это просто список)
|
||||
index_data = self.db.load_index_data()
|
||||
|
||||
# Если список пуст (БД пустая), безопасно выходим
|
||||
if not index_data:
|
||||
print("✅ База пуста.")
|
||||
self.template_ids = []
|
||||
self.embeddings = None
|
||||
return
|
||||
|
||||
raw_templates, _ = index_data
|
||||
|
||||
ids = []
|
||||
vecs = []
|
||||
for row in raw_templates:
|
||||
uid, _, emb_blob, _, _ = row
|
||||
ids.append(uid)
|
||||
vecs.append(np.frombuffer(emb_blob, dtype=np.float32))
|
||||
|
||||
self.template_ids = ids
|
||||
self.embeddings = np.array(vecs)
|
||||
print(f"✅ Готово. В индексе шаблонов: {len(self.template_ids)}")
|
||||
|
||||
def close(self):
|
||||
self.db.close()
|
||||
|
||||
# --- Утилиты ---
|
||||
|
||||
def _tokenize(self, text: str) -> List[str]:
|
||||
return [m.group() for m in self.master_regex.finditer(text)]
|
||||
|
||||
def _mask_for_search(self, text: str) -> str:
|
||||
text = self.mask_regex['guid'].sub('<GUID>', text)
|
||||
text = self.mask_regex['ip'].sub('<IP>', text)
|
||||
text = self.mask_regex['num'].sub('<NUM>', text)
|
||||
return text
|
||||
|
||||
def _detect_var_type(self, value: str) -> str:
|
||||
match = self.master_regex.fullmatch(value)
|
||||
return match.lastgroup if match and match.lastgroup in self.var_type_names else "VAR"
|
||||
|
||||
# --- Логика Кластеризации ---
|
||||
|
||||
def _find_best_match(self, input_vec: np.ndarray, log_text: str) -> Optional[int]:
|
||||
"""Ищет лучший шаблон по косинусному сходству, используя только RAM-индекс."""
|
||||
if self.embeddings is None or len(self.template_ids) == 0:
|
||||
return None
|
||||
|
||||
scores = util.cos_sim(input_vec, self.embeddings)[0]
|
||||
best_idx = scores.argmax().item()
|
||||
best_score = scores[best_idx].item()
|
||||
best_id = self.template_ids[best_idx]
|
||||
|
||||
if best_score > self.SCORE_EXACT_MATCH:
|
||||
return best_id
|
||||
|
||||
if best_score > self.SCORE_PARTIAL_MATCH:
|
||||
# Для проверки токенов придется подгрузить кандидата из БД
|
||||
cand = self. _load_template_from_db(best_id)
|
||||
cand_tokens = cand.get_tokens_as_str_list()
|
||||
new_tokens = self._tokenize(log_text)
|
||||
ratio = difflib.SequenceMatcher(None, cand_tokens, new_tokens).ratio()
|
||||
|
||||
if ratio > self.THRESHOLD_CREATE_NEW:
|
||||
return best_id
|
||||
|
||||
return None
|
||||
|
||||
def process(self, log_text: str) -> Dict[str, Any]:
|
||||
"""Основной пайплайн обработки лога."""
|
||||
masked_input = self._mask_for_search(log_text)
|
||||
input_vec = self.model.encode(masked_input)
|
||||
|
||||
best_id = self._find_best_match(input_vec, log_text)
|
||||
|
||||
if best_id is not None:
|
||||
# Шаблон найден -> Грузим его из БД (ленивая загрузка)
|
||||
template = self._load_template_from_db(best_id)
|
||||
|
||||
# Обновляем вектор скользящим средним
|
||||
n = template.hits
|
||||
updated_vec = (template.embedding * n + input_vec) / (n + 1)
|
||||
template.embedding = updated_vec
|
||||
|
||||
# Обновляем вектор в RAM
|
||||
idx = self.template_ids.index(best_id)
|
||||
self.embeddings[idx] = updated_vec
|
||||
|
||||
return self._update_and_extract(template, log_text)
|
||||
else:
|
||||
# Шаблон не найден -> Создаем новый
|
||||
return self._create_new_template(log_text, input_vec)
|
||||
|
||||
def process_time_measure(self,log_text: str) -> (float, float, float):
|
||||
"""Основной пайплайн обработки лога."""
|
||||
t1 = time.time()
|
||||
masked_input = self._mask_for_search(log_text)
|
||||
|
||||
input_vec = self.model.encode(masked_input)
|
||||
t2 = time.time()
|
||||
best_id = self._find_best_match(input_vec, log_text)
|
||||
|
||||
if best_id is not None:
|
||||
# Шаблон найден -> Грузим его из БД (ленивая загрузка)
|
||||
template = self._load_template_from_db(best_id)
|
||||
|
||||
# Обновляем вектор скользящим средним
|
||||
n = template.hits
|
||||
updated_vec = (template.embedding * n + input_vec) / (n + 1)
|
||||
template.embedding = updated_vec
|
||||
|
||||
# Обновляем вектор в RAM
|
||||
idx = self.template_ids.index(best_id)
|
||||
self.embeddings[idx] = updated_vec
|
||||
|
||||
t3 = time.time()
|
||||
self._update_and_extract(template, log_text)
|
||||
else:
|
||||
t3 = time.time()
|
||||
# Шаблон не найден -> Создаем новый
|
||||
self._create_new_template(log_text, input_vec)
|
||||
t4 = time.time()
|
||||
|
||||
return t2-t1, t3-t2, t4-t3
|
||||
|
||||
# --- Создание и обновление шаблонов ---
|
||||
|
||||
def _create_new_template(self, log_text: str, vector: np.ndarray) -> Dict[str, Any]:
|
||||
tokens = self._tokenize(log_text)
|
||||
new_tpl = LogTemplate(self.template_id_counter, tokens, log_text)
|
||||
new_tpl.embedding = vector
|
||||
|
||||
# Добавляем в RAM индекс
|
||||
self.template_ids.append(new_tpl.uid)
|
||||
if self.embeddings is None:
|
||||
self.embeddings = np.array([vector])
|
||||
else:
|
||||
self.embeddings = np.vstack([self.embeddings, vector])
|
||||
|
||||
self.template_id_counter += 1
|
||||
self.db.save_template(new_tpl)
|
||||
|
||||
return {
|
||||
'template_id': new_tpl.uid,
|
||||
'template_view': new_tpl.render(),
|
||||
'variables': [],
|
||||
'status': 'created'
|
||||
}
|
||||
|
||||
def _update_and_extract(self, template: LogTemplate, log_text: str) -> Dict[str, Any]:
|
||||
new_tokens = self._tokenize(log_text)
|
||||
old_tokens_str = template.get_tokens_as_str_list()
|
||||
|
||||
matcher = difflib.SequenceMatcher(None, old_tokens_str, new_tokens)
|
||||
updated_template_tokens = []
|
||||
extracted_variables = []
|
||||
|
||||
for tag, i1, i2, j1, j2 in matcher.get_opcodes():
|
||||
if tag == 'equal':
|
||||
updated_template_tokens.extend(template.tokens[i1:i2])
|
||||
|
||||
elif tag == 'replace':
|
||||
log_vals = new_tokens[j1:j2]
|
||||
tpl_seg = template.tokens[i1:i2]
|
||||
|
||||
# Если заменяем существующую переменную
|
||||
if len(tpl_seg) == 1 and isinstance(tpl_seg[0], LogVariable):
|
||||
var = tpl_seg[0]
|
||||
full_text = "".join(log_vals)
|
||||
|
||||
is_bloated = len(full_text) > self.MAX_VAR_LEN
|
||||
has_hard = any(t.strip() in self.HARD_DELIMITERS for t in log_vals)
|
||||
has_space = any(t.isspace() for t in log_vals)
|
||||
has_soft = any(t.strip() in self.SOFT_DELIMITERS for t in log_vals)
|
||||
|
||||
if has_hard or has_space or (is_bloated and has_soft):
|
||||
decomposed, new_vars = self._decompose_segment(log_vals, template, var.initial_value)
|
||||
updated_template_tokens.extend(decomposed)
|
||||
extracted_variables.extend(new_vars)
|
||||
else:
|
||||
updated_template_tokens.append(var)
|
||||
if full_text != var.initial_value:
|
||||
extracted_variables.append(self._make_delta(var, full_text))
|
||||
else:
|
||||
# Заменяем текст -> формируем новые переменные
|
||||
init_hint = "".join(t.initial_value if isinstance(t, LogVariable) else str(t) for t in tpl_seg)
|
||||
decomposed, new_vars = self._decompose_segment(log_vals, template, init_hint)
|
||||
updated_template_tokens.extend(decomposed)
|
||||
extracted_variables.extend(new_vars)
|
||||
|
||||
elif tag == 'delete':
|
||||
tpl_seg = template.tokens[i1:i2]
|
||||
if len(tpl_seg) == 1 and isinstance(tpl_seg[0], LogVariable):
|
||||
var = tpl_seg[0]
|
||||
updated_template_tokens.append(var)
|
||||
if var.initial_value != "":
|
||||
extracted_variables.append(self._make_delta(var, ""))
|
||||
else:
|
||||
new_var = LogVariable(template.get_next_var_id(), initial_value="".join(str(t) for t in tpl_seg))
|
||||
updated_template_tokens.append(new_var)
|
||||
if new_var.initial_value != "":
|
||||
extracted_variables.append(self._make_delta(new_var, ""))
|
||||
|
||||
elif tag == 'insert':
|
||||
decomposed, new_vars = self._decompose_segment(new_tokens[j1:j2], template, "")
|
||||
updated_template_tokens.extend(decomposed)
|
||||
extracted_variables.extend(new_vars)
|
||||
|
||||
template.tokens = updated_template_tokens
|
||||
template.hits += 1
|
||||
self.db.save_template(template)
|
||||
|
||||
return {
|
||||
'template_id': template.uid,
|
||||
'template_view': template.render(),
|
||||
'variables': extracted_variables,
|
||||
'status': 'updated'
|
||||
}
|
||||
|
||||
# --- Вспомогательные методы для логики извлечения ---
|
||||
|
||||
def _decompose_segment(self, tokens_list: List[str], template: LogTemplate, initial_hint: str):
|
||||
"""Разбивает сегмент на переменные и статические токены."""
|
||||
full_text = "".join(tokens_list)
|
||||
is_bloated = len(full_text) > self.MAX_VAR_LEN
|
||||
|
||||
result_structure = []
|
||||
extracted_vars = []
|
||||
current_var_tokens = []
|
||||
|
||||
def flush_var():
|
||||
if not current_var_tokens:
|
||||
return
|
||||
val = "".join(current_var_tokens)
|
||||
v_type = self._detect_var_type(val)
|
||||
init = initial_hint if len(result_structure) == 0 else ""
|
||||
|
||||
new_v = LogVariable(template.get_next_var_id(), initial_value=init, var_type=v_type)
|
||||
result_structure.append(new_v)
|
||||
|
||||
if val != new_v.initial_value:
|
||||
extracted_vars.append(self._make_delta(new_v, val))
|
||||
current_var_tokens.clear()
|
||||
|
||||
for token in tokens_list:
|
||||
t_strip = token.strip()
|
||||
should_split = (t_strip in self.HARD_DELIMITERS) or token.isspace() or (
|
||||
is_bloated and t_strip in self.SOFT_DELIMITERS)
|
||||
|
||||
if should_split:
|
||||
flush_var()
|
||||
result_structure.append(token)
|
||||
else:
|
||||
current_var_tokens.append(token)
|
||||
|
||||
flush_var()
|
||||
return result_structure, extracted_vars
|
||||
|
||||
def _make_delta(self, var: LogVariable, actual_value: str) -> Dict[str, Any]:
|
||||
"""Формирует словарь дельты (изменения) для переменной."""
|
||||
return {
|
||||
'uid': var.uid,
|
||||
'name': str(var),
|
||||
'value': actual_value,
|
||||
'initial': var.initial_value
|
||||
}
|
||||
|
||||
# --- Интеграция с БД (Ленивая загрузка) ---
|
||||
def _load_template_from_db(self, uid: int) -> LogTemplate:
|
||||
"""Восстанавливает конкретный шаблон из БД."""
|
||||
row, vars_map = self.db.get_template_data_by_id(uid)
|
||||
|
||||
if not row:
|
||||
raise ValueError(f"Шаблон с ID {uid} не найден в БД!")
|
||||
|
||||
template_id, pattern, emb_blob, hits, local_cnt = row
|
||||
|
||||
# Передаем vars_map напрямую, так как там уже лежат переменные только этого шаблона
|
||||
tokens = self._hydrate_pattern(pattern, vars_map)
|
||||
|
||||
tpl = LogTemplate(template_id, tokens, pattern)
|
||||
tpl.embedding = np.frombuffer(emb_blob, dtype=np.float32)
|
||||
tpl.hits = hits
|
||||
tpl.local_var_counter = local_cnt
|
||||
|
||||
return tpl
|
||||
|
||||
def _hydrate_pattern(self, pattern: str, tpl_vars: Dict[int, LogVariable]) -> List:
|
||||
parts = re.split(r'(<[A-Z]+_\d+>)', pattern)
|
||||
tokens = []
|
||||
for part in parts:
|
||||
if not part: continue
|
||||
|
||||
if part.startswith('<') and part.endswith('>'):
|
||||
match = re.match(r'<([A-Z]+)_(\d+)>', part)
|
||||
if match:
|
||||
v_type, v_id_str = match.groups()
|
||||
v_id = int(v_id_str)
|
||||
|
||||
if v_id in tpl_vars:
|
||||
tokens.append(tpl_vars[v_id])
|
||||
else:
|
||||
tokens.append(LogVariable(v_id, var_type=v_type))
|
||||
continue
|
||||
|
||||
tokens.extend(self._tokenize(part))
|
||||
return tokens
|
||||
|
||||
if __name__ == '__main__':
|
||||
MODEL_PATH = '../Resources/model'
|
||||
DB_FILE = "logs.db"
|
||||
|
||||
if os.path.exists(DB_FILE):
|
||||
os.remove(DB_FILE)
|
||||
|
||||
print("--- ЗАПУСК: Delta Mode ---")
|
||||
clusterer = StreamingLogCluster(MODEL_PATH, db_path=DB_FILE)
|
||||
|
||||
# 1. Создаем шаблон.
|
||||
# Переменных нет, так как все значения становятся "дефолтными" (initial).
|
||||
log1 = "2025-01-01 User admin login"
|
||||
res1 = clusterer.process(log1)
|
||||
print(f"Log 1: {log1} -> ID: {res1['template_id']}")
|
||||
print(f" VARS (Delta): {res1['variables']}")
|
||||
# Ожидание: [], так как при создании шаблона текущие значения становятся Initial.
|
||||
|
||||
# 2. Меняем admin -> guest.
|
||||
# Должна вернуться ТОЛЬКО переменная гостя. Дата та же - она не вернется!
|
||||
log2 = "2025-01-01 User guest login"
|
||||
res2 = clusterer.process(log2)
|
||||
print(f"\nLog 2: {log2} -> ID: {res2['template_id']}")
|
||||
|
||||
# Красивый вывод дельты
|
||||
if res2['variables']:
|
||||
print(" CHANGES DETECTED:")
|
||||
for v in res2['variables']:
|
||||
print(f" * {v['name']} changed from '{v['initial']}' to '{v['value']}'")
|
||||
else:
|
||||
print(" NO CHANGES (Full match with template defaults)")
|
||||
|
||||
# 3. Меняем всё (Дата + Юзер)
|
||||
log3 = "2025-02-02 User root login"
|
||||
res3 = clusterer.process(log3)
|
||||
print(f"\nLog 3: {log3} -> ID: {res3['template_id']}")
|
||||
if res3['variables']:
|
||||
print(" CHANGES DETECTED:")
|
||||
for v in res3['variables']:
|
||||
print(f" * {v['name']} ('{v['initial']}') to '{v['value']}'")
|
||||
|
||||
# 4. Возвращаемся к оригиналу (admin + старая дата)
|
||||
# Должен вернуться пустой список, так как это идеальное совпадение с Initials
|
||||
log4 = "2025-01-01 User admin login"
|
||||
res4 = clusterer.process(log4)
|
||||
print(f"\nLog 4 (Revert): {log4} -> ID: {res4['template_id']}")
|
||||
print(f" VARS (Delta): {res4['variables']}")
|
||||
122
Processor/TemplateDatabase.py
Normal file
122
Processor/TemplateDatabase.py
Normal file
@@ -0,0 +1,122 @@
|
||||
import re
|
||||
import sqlite3
|
||||
import numpy as np
|
||||
from typing import List, Dict, Tuple, Optional
|
||||
|
||||
from Processor.Models.LogTemplate import LogTemplate
|
||||
from Processor.Models.LogVariable import LogVariable
|
||||
|
||||
|
||||
class TemplateDatabase:
|
||||
def __init__(self, db_path: str = "logs_knowledge.db"):
|
||||
self.conn = sqlite3.connect(db_path, check_same_thread=False)
|
||||
self.create_tables()
|
||||
|
||||
def create_tables(self):
|
||||
with self.conn:
|
||||
self.conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS templates (
|
||||
id INTEGER PRIMARY KEY,
|
||||
pattern TEXT NOT NULL,
|
||||
embedding BLOB NOT NULL,
|
||||
hits INTEGER DEFAULT 1,
|
||||
local_counter INTEGER DEFAULT 1
|
||||
)
|
||||
""")
|
||||
self.conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS variables (
|
||||
template_id INTEGER,
|
||||
local_id INTEGER,
|
||||
var_type TEXT,
|
||||
initial_value TEXT,
|
||||
PRIMARY KEY (template_id, local_id),
|
||||
FOREIGN KEY(template_id) REFERENCES templates(id) ON DELETE CASCADE
|
||||
)
|
||||
""")
|
||||
|
||||
def save_template(self, tpl: LogTemplate):
|
||||
emb_bytes = tpl.embedding.astype(np.float32).tobytes()
|
||||
pattern_str = tpl.render()
|
||||
|
||||
with self.conn:
|
||||
self.conn.execute("""
|
||||
INSERT INTO templates (id, pattern, embedding, hits, local_counter)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
pattern = excluded.pattern,
|
||||
embedding = excluded.embedding,
|
||||
hits = excluded.hits,
|
||||
local_counter = excluded.local_counter
|
||||
""", (tpl.uid, pattern_str, emb_bytes, tpl.hits, tpl.local_var_counter))
|
||||
|
||||
self.conn.execute("DELETE FROM variables WHERE template_id = ?", (tpl.uid,))
|
||||
|
||||
vars_data = []
|
||||
for token in tpl.tokens:
|
||||
if isinstance(token, LogVariable):
|
||||
vars_data.append((tpl.uid, token.uid, token.var_type, token.initial_value))
|
||||
|
||||
if vars_data:
|
||||
self.conn.executemany("INSERT INTO variables VALUES (?, ?, ?, ?)", vars_data)
|
||||
|
||||
# --- НОВЫЕ МЕТОДЫ ДЛЯ ОПТИМИЗАЦИИ ОЗУ ---
|
||||
|
||||
def load_index_data(self) -> List[Tuple[int, bytes]]:
|
||||
"""
|
||||
Загружает ТОЛЬКО идентификаторы и эмбеддинги.
|
||||
Используется при старте приложения для построения RAM-индекса.
|
||||
"""
|
||||
cursor = self.conn.execute("SELECT id, embedding FROM templates")
|
||||
return cursor.fetchall()
|
||||
|
||||
def get_template_data_by_id(self, template_id: int) -> Tuple[Optional[Tuple], Dict[int, LogVariable]]:
|
||||
"""
|
||||
Точечно загружает сырые данные ОДНОГО шаблона по его ID.
|
||||
Возвращает: (row_шаблона, словарь_переменных)
|
||||
"""
|
||||
# 1. Загружаем сам шаблон
|
||||
cursor = self.conn.execute(
|
||||
"SELECT id, pattern, embedding, hits, local_counter FROM templates WHERE id = ?",
|
||||
(template_id,)
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
|
||||
if not row:
|
||||
return None, {}
|
||||
|
||||
# 2. Загружаем его переменные
|
||||
vars_cursor = self.conn.execute(
|
||||
"SELECT local_id, var_type, initial_value FROM variables WHERE template_id = ?",
|
||||
(template_id,)
|
||||
)
|
||||
|
||||
vars_map = {}
|
||||
for v_row in vars_cursor:
|
||||
l_id, v_type, init_val = v_row
|
||||
vars_map[l_id] = LogVariable(l_id, initial_value=init_val, var_type=v_type)
|
||||
|
||||
return row, vars_map
|
||||
|
||||
|
||||
def load_raw_data(self):
|
||||
"""Возвращает все данные целиком. (Осторожно: может забить ОЗУ при большом объеме БД)"""
|
||||
cursor = self.conn.execute("SELECT template_id, local_id, var_type, initial_value FROM variables")
|
||||
vars_map = {}
|
||||
for row in cursor:
|
||||
t_id, l_id, v_type, init_val = row
|
||||
if t_id not in vars_map: vars_map[t_id] = {}
|
||||
vars_map[t_id][l_id] = LogVariable(l_id, initial_value=init_val, var_type=v_type)
|
||||
|
||||
templates_data = []
|
||||
cursor = self.conn.execute("SELECT id, pattern, embedding, hits, local_counter FROM templates")
|
||||
for row in cursor:
|
||||
templates_data.append(row)
|
||||
|
||||
return templates_data, vars_map
|
||||
|
||||
def get_max_id(self) -> int:
|
||||
res = self.conn.execute("SELECT MAX(id) FROM templates").fetchone()[0]
|
||||
return res if res else 0
|
||||
|
||||
def close(self):
|
||||
self.conn.close()
|
||||
BIN
Processor/logs.db
Normal file
BIN
Processor/logs.db
Normal file
Binary file not shown.
BIN
Resources/logs.db
Normal file
BIN
Resources/logs.db
Normal file
Binary file not shown.
10
Resources/model/1_Pooling/config.json
Normal file
10
Resources/model/1_Pooling/config.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"word_embedding_dimension": 384,
|
||||
"pooling_mode_cls_token": false,
|
||||
"pooling_mode_mean_tokens": true,
|
||||
"pooling_mode_max_tokens": false,
|
||||
"pooling_mode_mean_sqrt_len_tokens": false,
|
||||
"pooling_mode_weightedmean_tokens": false,
|
||||
"pooling_mode_lasttoken": false,
|
||||
"include_prompt": true
|
||||
}
|
||||
409
Resources/model/README.md
Normal file
409
Resources/model/README.md
Normal file
@@ -0,0 +1,409 @@
|
||||
---
|
||||
tags:
|
||||
- sentence-transformers
|
||||
- sentence-similarity
|
||||
- feature-extraction
|
||||
- dense
|
||||
- generated_from_trainer
|
||||
- dataset_size:2400
|
||||
- loss:TripletLoss
|
||||
base_model: sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
|
||||
widget:
|
||||
- source_sentence: id=certification<NUM>@yahoo.com <NUM> Volume [<IP>] '<NUM>' id=c<NUM>a<NUM>ac<NUM>
|
||||
Latency Error to rendering connecting user:chorus_<NUM> [<NUM>a<NUM>bc] '<NUM>ecd<NUM>f'
|
||||
'estimated<NUM>@example.org' started together [<NUM><NUM><NUM>] user:trying<NUM>@yandex.com
|
||||
present <NUM> id=<NUM>c<NUM>b<NUM>ad
|
||||
sentences:
|
||||
- '''<NUM>'';<NUM><NUM><NUM>;goals;failed;Client;''<IP>'';Directory;killing;licence<NUM>@gmail.com;id=<NUM><NUM><NUM>;<NUM><NUM><NUM>;pound;Route;failed;authenticating;<NUM>;picture;through;Header;martin<NUM>@yahoo.com;<IP>;/var/log/unit.jpg;Route;deleted'
|
||||
- id=positioning<NUM>@example.com;confidential;'/var/log/offer.awk';'/var/log/contain.dat';id=<NUM>;id=cute<NUM>@protonmail.com;'<NUM>';Packet;'<NUM>';locked;either;with;Transaction;updated;'<NUM>.<NUM>'
|
||||
- id=collaboration<NUM>@example.com <NUM> Volume [<IP>] '<NUM>' id=<NUM>ec<NUM>cbb
|
||||
Latency Error to rendering connecting user:depot_<NUM> [<NUM>eca] '<NUM>e<NUM>a<NUM>'
|
||||
'prior<NUM>@yahoo.com' started together [<NUM><NUM><NUM>] user:solaris<NUM>@outlook.com
|
||||
present <NUM> id=<NUM>b<NUM>d<NUM>
|
||||
- source_sentence: remote user:robbie_<NUM> <NUM> fundamental id=<NUM> User aborted
|
||||
user:/var/log/with.jpeg through '/var/log/love.md' cycling '<NUM>.<NUM>' private
|
||||
'<NUM>.<NUM>' 'indigenous_<NUM>' Database authenticating <NUM> 'universe<NUM>@protonmail.com'
|
||||
Query <NUM> id=chris_<NUM> names
|
||||
sentences:
|
||||
- user:/var/log/silver.doc <NUM> User remote <NUM> names aborted 'smoke<NUM>@duck.com'
|
||||
<NUM> authenticating '<NUM>.<NUM>' private cycling user:alto_<NUM> '<NUM>.<NUM>'
|
||||
id=<NUM> Query fundamental Database '/var/log/wall.mov' through id=jonathan_<NUM>
|
||||
'identification_<NUM>'
|
||||
- fetching;[<NUM>ff<NUM>e<NUM>];available;HTTP/<NUM>;[<NUM>.<NUM>];POST;user:<NUM>.<NUM>;<NUM><NUM><NUM>;user:<NUM>;<NUM>.<NUM>;Session;System;user:san<NUM>@outlook.com;had;'<NUM>';user:/var/log/rich.tar.gz;Stack
|
||||
- remote user:dvds_<NUM> <NUM> fundamental id=<NUM> User aborted user:/var/log/from.csv
|
||||
through '/var/log/foot.dat' cycling '<NUM>.<NUM>' private '<NUM>.<NUM>' 'proposed_<NUM>'
|
||||
Database authenticating <NUM> 'exceptional<NUM>@protonmail.com' Query <NUM> id=website_<NUM>
|
||||
names
|
||||
- source_sentence: projection;local;insecure;Thread;'<IP>';<IP>;[<NUM>];with;Interface;Buffer;updated;'/var/log/write.bmp';user:clearly_<NUM>;active;afford;id=<NUM>ab<NUM>;Latency;[strain<NUM>@live.com];stupid<NUM>@gmail.com;Key;created
|
||||
sentences:
|
||||
- projection;local;insecure;Thread;'<IP>';<IP>;[<NUM>];with;Interface;Buffer;updated;'/var/log/shoe.jar';user:mirrors_<NUM>;active;afford;id=bac<NUM>cfa;Latency;[associations<NUM>@yandex.com];laos<NUM>@example.org;Key;created
|
||||
- '''commercial_<NUM>''|''/var/log/piece.tar.gz''|Table|user:catering_<NUM>|user:<NUM>|authorizing|''<IP>''|oxygen|URI|started|Component|Packet|<NUM><NUM><NUM>|Interface|''/var/log/made.exe''|GET|user:resist<NUM>@yahoo.com|Payload|[<NUM>]'
|
||||
- Port|user:pdf_<NUM>|<NUM>|user:<NUM>.<NUM>|[<NUM>f<NUM>c<NUM>dc]|'adb<NUM>e<NUM>'|implementing|user:<NUM>cfb<NUM>e<NUM>a|<NUM>.<NUM>|discussed|<NUM>|Memory|id=/var/log/dance.m<NUM>u|<NUM>.<NUM>|ceo|remote|'<NUM>.<NUM>'|user:<NUM>a<NUM>|JS
|
||||
- source_sentence: updated|national|rendering|comply|user:<NUM>|binding|Gateway|<IP>|resolving|responsible|[<NUM>]|'opportunities<NUM>@duck.com'|opens_<NUM>|JSON|retrying|Server|Error|'<NUM>ec<NUM>ca'|berkeley|id=<NUM>.<NUM>|System|torture|Job|id=f<NUM>d
|
||||
sentences:
|
||||
- connecting disconnected comes<NUM>@gmail.com unavailable Directory [/var/log/early.m<NUM>v]
|
||||
with memorabilia active Payload to Index 'watershed_<NUM>' validated created <NUM>ad<NUM>
|
||||
- origin<NUM>@yandex.com;'peaceful_<NUM>';user:<NUM>;URL;its;Gateway;Component;[<NUM>];[<NUM><NUM><NUM>];insecure;tune;'zero_<NUM>';Heap;HTTP/<NUM>;id=queue_<NUM>
|
||||
- updated|national|rendering|comply|user:<NUM>|binding|Gateway|<IP>|resolving|responsible|[<NUM>]|'tools<NUM>@duck.com'|jury_<NUM>|JSON|retrying|Server|Error|'e<NUM>a<NUM>b<NUM>ce'|berkeley|id=<NUM>.<NUM>|System|torture|Job|id=bb<NUM>bc
|
||||
- source_sentence: authenticating YAML PATCH authorizing id=/var/log/seem.tar.xz [<NUM>]
|
||||
rendering 'pursue_<NUM>' [<NUM><NUM><NUM>] fresh online authenticating GET Heap
|
||||
CRITICAL Module id=bother_<NUM>
|
||||
sentences:
|
||||
- authenticating YAML PATCH authorizing id=/var/log/born.log [<NUM>] rendering 'school_<NUM>'
|
||||
[<NUM><NUM><NUM>] fresh online authenticating GET Heap CRITICAL Module id=brochure_<NUM>
|
||||
- user:<IP>;completed;<NUM>;id=/var/log/whose.jpg;user:<NUM>.<NUM>;resolving;allowed;Commit;Index;Daemon;building;length;hall;[/var/log/segment.doc];with
|
||||
- Heap;id=dim_<NUM>;[except<NUM>@gmail.com];dropped;determination;via;File;created;id=<NUM>;unavailable;id=/var/log/page.tar.xz;rendering;<NUM>b<NUM>ad<NUM>;id=/var/log/want.tar.gz;Kernel;JS;secure;HTTP/<NUM>;user:a<NUM>dd<NUM>d;user:<NUM><NUM><NUM>;resolving;Header
|
||||
pipeline_tag: sentence-similarity
|
||||
library_name: sentence-transformers
|
||||
metrics:
|
||||
- cosine_accuracy
|
||||
model-index:
|
||||
- name: SentenceTransformer based on sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
|
||||
results:
|
||||
- task:
|
||||
type: triplet
|
||||
name: Triplet
|
||||
dataset:
|
||||
name: structural val
|
||||
type: structural-val
|
||||
metrics:
|
||||
- type: cosine_accuracy
|
||||
value: 0.996666669845581
|
||||
name: Cosine Accuracy
|
||||
---
|
||||
|
||||
# SentenceTransformer based on sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
|
||||
|
||||
This is a [sentence-transformers](https://www.SBERT.net) model finetuned from [sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2](https://huggingface.co/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2). It maps sentences & paragraphs to a 384-dimensional dense vector space and can be used for semantic textual similarity, semantic search, paraphrase mining, text classification, clustering, and more.
|
||||
|
||||
## Model Details
|
||||
|
||||
### Model Description
|
||||
- **Model Type:** Sentence Transformer
|
||||
- **Base model:** [sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2](https://huggingface.co/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2) <!-- at revision 86741b4e3f5cb7765a600d3a3d55a0f6a6cb443d -->
|
||||
- **Maximum Sequence Length:** 128 tokens
|
||||
- **Output Dimensionality:** 384 dimensions
|
||||
- **Similarity Function:** Cosine Similarity
|
||||
<!-- - **Training Dataset:** Unknown -->
|
||||
<!-- - **Language:** Unknown -->
|
||||
<!-- - **License:** Unknown -->
|
||||
|
||||
### Model Sources
|
||||
|
||||
- **Documentation:** [Sentence Transformers Documentation](https://sbert.net)
|
||||
- **Repository:** [Sentence Transformers on GitHub](https://github.com/huggingface/sentence-transformers)
|
||||
- **Hugging Face:** [Sentence Transformers on Hugging Face](https://huggingface.co/models?library=sentence-transformers)
|
||||
|
||||
### Full Model Architecture
|
||||
|
||||
```
|
||||
SentenceTransformer(
|
||||
(0): Transformer({'max_seq_length': 128, 'do_lower_case': False, 'architecture': 'BertModel'})
|
||||
(1): Pooling({'word_embedding_dimension': 384, 'pooling_mode_cls_token': False, 'pooling_mode_mean_tokens': True, 'pooling_mode_max_tokens': False, 'pooling_mode_mean_sqrt_len_tokens': False, 'pooling_mode_weightedmean_tokens': False, 'pooling_mode_lasttoken': False, 'include_prompt': True})
|
||||
)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Direct Usage (Sentence Transformers)
|
||||
|
||||
First install the Sentence Transformers library:
|
||||
|
||||
```bash
|
||||
pip install -U sentence-transformers
|
||||
```
|
||||
|
||||
Then you can load this model and run inference.
|
||||
```python
|
||||
from sentence_transformers import SentenceTransformer
|
||||
|
||||
# Download from the 🤗 Hub
|
||||
model = SentenceTransformer("sentence_transformers_model_id")
|
||||
# Run inference
|
||||
sentences = [
|
||||
"authenticating YAML PATCH authorizing id=/var/log/seem.tar.xz [<NUM>] rendering 'pursue_<NUM>' [<NUM><NUM><NUM>] fresh online authenticating GET Heap CRITICAL Module id=bother_<NUM>",
|
||||
"authenticating YAML PATCH authorizing id=/var/log/born.log [<NUM>] rendering 'school_<NUM>' [<NUM><NUM><NUM>] fresh online authenticating GET Heap CRITICAL Module id=brochure_<NUM>",
|
||||
'Heap;id=dim_<NUM>;[except<NUM>@gmail.com];dropped;determination;via;File;created;id=<NUM>;unavailable;id=/var/log/page.tar.xz;rendering;<NUM>b<NUM>ad<NUM>;id=/var/log/want.tar.gz;Kernel;JS;secure;HTTP/<NUM>;user:a<NUM>dd<NUM>d;user:<NUM><NUM><NUM>;resolving;Header',
|
||||
]
|
||||
embeddings = model.encode(sentences)
|
||||
print(embeddings.shape)
|
||||
# [3, 384]
|
||||
|
||||
# Get the similarity scores for the embeddings
|
||||
similarities = model.similarity(embeddings, embeddings)
|
||||
print(similarities)
|
||||
# tensor([[ 1.0000, 0.9960, -0.1292],
|
||||
# [ 0.9960, 1.0000, -0.1269],
|
||||
# [-0.1292, -0.1269, 1.0000]])
|
||||
```
|
||||
|
||||
<!--
|
||||
### Direct Usage (Transformers)
|
||||
|
||||
<details><summary>Click to see the direct usage in Transformers</summary>
|
||||
|
||||
</details>
|
||||
-->
|
||||
|
||||
<!--
|
||||
### Downstream Usage (Sentence Transformers)
|
||||
|
||||
You can finetune this model on your own dataset.
|
||||
|
||||
<details><summary>Click to expand</summary>
|
||||
|
||||
</details>
|
||||
-->
|
||||
|
||||
<!--
|
||||
### Out-of-Scope Use
|
||||
|
||||
*List how the model may foreseeably be misused and address what users ought not to do with the model.*
|
||||
-->
|
||||
|
||||
## Evaluation
|
||||
|
||||
### Metrics
|
||||
|
||||
#### Triplet
|
||||
|
||||
* Dataset: `structural-val`
|
||||
* Evaluated with [<code>TripletEvaluator</code>](https://sbert.net/docs/package_reference/sentence_transformer/evaluation.html#sentence_transformers.evaluation.TripletEvaluator)
|
||||
|
||||
| Metric | Value |
|
||||
|:--------------------|:-----------|
|
||||
| **cosine_accuracy** | **0.9967** |
|
||||
|
||||
<!--
|
||||
## Bias, Risks and Limitations
|
||||
|
||||
*What are the known or foreseeable issues stemming from this model? You could also flag here known failure cases or weaknesses of the model.*
|
||||
-->
|
||||
|
||||
<!--
|
||||
### Recommendations
|
||||
|
||||
*What are recommendations with respect to the foreseeable issues? For example, filtering explicit content.*
|
||||
-->
|
||||
|
||||
## Training Details
|
||||
|
||||
### Training Dataset
|
||||
|
||||
#### Unnamed Dataset
|
||||
|
||||
* Size: 2,400 training samples
|
||||
* Columns: <code>sentence_0</code>, <code>sentence_1</code>, and <code>sentence_2</code>
|
||||
* Approximate statistics based on the first 1000 samples:
|
||||
| | sentence_0 | sentence_1 | sentence_2 |
|
||||
|:--------|:------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------|
|
||||
| type | string | string | string |
|
||||
| details | <ul><li>min: 31 tokens</li><li>mean: 81.66 tokens</li><li>max: 128 tokens</li></ul> | <ul><li>min: 33 tokens</li><li>mean: 81.55 tokens</li><li>max: 128 tokens</li></ul> | <ul><li>min: 28 tokens</li><li>mean: 79.74 tokens</li><li>max: 128 tokens</li></ul> |
|
||||
* Samples:
|
||||
| sentence_0 | sentence_1 | sentence_2 |
|
||||
|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| <code>ERROR;[river_<NUM>];<IP>;bit;<NUM>.<NUM>;watches;Table;user:<NUM>.<NUM>;/var/log/art.zip;/var/log/neck.docx;id=<NUM><NUM><NUM>;<NUM>.<NUM>;schedules;watson_<NUM>;DELETE;user:<NUM>.<NUM>;Session</code> | <code>ERROR;[taxation_<NUM>];<IP>;bit;<NUM>.<NUM>;watches;Table;user:<NUM>.<NUM>;/var/log/hunt.pps;/var/log/radio.<NUM>z;id=<NUM><NUM><NUM>;<NUM>.<NUM>;schedules;tab_<NUM>;DELETE;user:<NUM>.<NUM>;Session</code> | <code>[experiments_<NUM>] id=<NUM><NUM><NUM> watches DELETE Table user:<NUM>.<NUM> <NUM>.<NUM> <NUM>.<NUM> need_<NUM> /var/log/list.mov <IP> user:<NUM>.<NUM> schedules Session /var/log/pull.pptx bit ERROR</code> |
|
||||
| <code>divided;defence;binding;user:helmet<NUM>@outlook.com;hours;user:<IP>;parsing;rocky;API;Gateway;started;by;flexible;by;INFO;Interface;Memory;teens;JS;fetching;deleted</code> | <code>divided;defence;binding;user:night<NUM>@protonmail.com;hours;user:<IP>;parsing;rocky;API;Gateway;started;by;flexible;by;INFO;Interface;Memory;teens;JS;fetching;deleted</code> | <code>by;binding;Interface;user:<IP>;divided;INFO;parsing;API;Memory;teens;user:cells<NUM>@example.org;started;Gateway;by;deleted;JS;defence;hours;fetching;flexible;rocky</code> |
|
||||
| <code>user:c<NUM>ed<NUM>\|queued\|<NUM>\|private\|Session\|blocked\|at\|user:<NUM>b<NUM>ba\|<NUM>.<NUM>\|Rollback\|Config\|<NUM><NUM><NUM>\|Config\|user:margin<NUM>@example.com\|spawning\|<NUM>\|inactive</code> | <code>user:<NUM>ae<NUM>\|queued\|<NUM>\|private\|Session\|blocked\|at\|user:<NUM>db<NUM>ce\|<NUM>.<NUM>\|Rollback\|Config\|<NUM><NUM><NUM>\|Config\|user:travelers<NUM>@yandex.com\|spawning\|<NUM>\|inactive</code> | <code><NUM>;spawning;inactive;<NUM><NUM><NUM>;user:d<NUM>ce<NUM>;queued;Config;<NUM>;user:promote<NUM>@protonmail.com;Config;private;user:f<NUM>ad<NUM>;at;Session;<NUM>.<NUM>;blocked;Rollback</code> |
|
||||
* Loss: [<code>TripletLoss</code>](https://sbert.net/docs/package_reference/sentence_transformer/losses.html#tripletloss) with these parameters:
|
||||
```json
|
||||
{
|
||||
"distance_metric": "TripletDistanceMetric.COSINE",
|
||||
"triplet_margin": 0.5
|
||||
}
|
||||
```
|
||||
|
||||
### Training Hyperparameters
|
||||
#### Non-Default Hyperparameters
|
||||
|
||||
- `per_device_train_batch_size`: 64
|
||||
- `per_device_eval_batch_size`: 64
|
||||
- `multi_dataset_batch_sampler`: round_robin
|
||||
|
||||
#### All Hyperparameters
|
||||
<details><summary>Click to expand</summary>
|
||||
|
||||
- `overwrite_output_dir`: False
|
||||
- `do_predict`: False
|
||||
- `eval_strategy`: no
|
||||
- `prediction_loss_only`: True
|
||||
- `per_device_train_batch_size`: 64
|
||||
- `per_device_eval_batch_size`: 64
|
||||
- `per_gpu_train_batch_size`: None
|
||||
- `per_gpu_eval_batch_size`: None
|
||||
- `gradient_accumulation_steps`: 1
|
||||
- `eval_accumulation_steps`: None
|
||||
- `torch_empty_cache_steps`: None
|
||||
- `learning_rate`: 5e-05
|
||||
- `weight_decay`: 0.0
|
||||
- `adam_beta1`: 0.9
|
||||
- `adam_beta2`: 0.999
|
||||
- `adam_epsilon`: 1e-08
|
||||
- `max_grad_norm`: 1
|
||||
- `num_train_epochs`: 3
|
||||
- `max_steps`: -1
|
||||
- `lr_scheduler_type`: linear
|
||||
- `lr_scheduler_kwargs`: {}
|
||||
- `warmup_ratio`: 0.0
|
||||
- `warmup_steps`: 0
|
||||
- `log_level`: passive
|
||||
- `log_level_replica`: warning
|
||||
- `log_on_each_node`: True
|
||||
- `logging_nan_inf_filter`: True
|
||||
- `save_safetensors`: True
|
||||
- `save_on_each_node`: False
|
||||
- `save_only_model`: False
|
||||
- `restore_callback_states_from_checkpoint`: False
|
||||
- `no_cuda`: False
|
||||
- `use_cpu`: False
|
||||
- `use_mps_device`: False
|
||||
- `seed`: 42
|
||||
- `data_seed`: None
|
||||
- `jit_mode_eval`: False
|
||||
- `bf16`: False
|
||||
- `fp16`: False
|
||||
- `fp16_opt_level`: O1
|
||||
- `half_precision_backend`: auto
|
||||
- `bf16_full_eval`: False
|
||||
- `fp16_full_eval`: False
|
||||
- `tf32`: None
|
||||
- `local_rank`: 0
|
||||
- `ddp_backend`: None
|
||||
- `tpu_num_cores`: None
|
||||
- `tpu_metrics_debug`: False
|
||||
- `debug`: []
|
||||
- `dataloader_drop_last`: False
|
||||
- `dataloader_num_workers`: 0
|
||||
- `dataloader_prefetch_factor`: None
|
||||
- `past_index`: -1
|
||||
- `disable_tqdm`: False
|
||||
- `remove_unused_columns`: True
|
||||
- `label_names`: None
|
||||
- `load_best_model_at_end`: False
|
||||
- `ignore_data_skip`: False
|
||||
- `fsdp`: []
|
||||
- `fsdp_min_num_params`: 0
|
||||
- `fsdp_config`: {'min_num_params': 0, 'xla': False, 'xla_fsdp_v2': False, 'xla_fsdp_grad_ckpt': False}
|
||||
- `fsdp_transformer_layer_cls_to_wrap`: None
|
||||
- `accelerator_config`: {'split_batches': False, 'dispatch_batches': None, 'even_batches': True, 'use_seedable_sampler': True, 'non_blocking': False, 'gradient_accumulation_kwargs': None}
|
||||
- `parallelism_config`: None
|
||||
- `deepspeed`: None
|
||||
- `label_smoothing_factor`: 0.0
|
||||
- `optim`: adamw_torch
|
||||
- `optim_args`: None
|
||||
- `adafactor`: False
|
||||
- `group_by_length`: False
|
||||
- `length_column_name`: length
|
||||
- `project`: huggingface
|
||||
- `trackio_space_id`: trackio
|
||||
- `ddp_find_unused_parameters`: None
|
||||
- `ddp_bucket_cap_mb`: None
|
||||
- `ddp_broadcast_buffers`: False
|
||||
- `dataloader_pin_memory`: True
|
||||
- `dataloader_persistent_workers`: False
|
||||
- `skip_memory_metrics`: True
|
||||
- `use_legacy_prediction_loop`: False
|
||||
- `push_to_hub`: False
|
||||
- `resume_from_checkpoint`: None
|
||||
- `hub_model_id`: None
|
||||
- `hub_strategy`: every_save
|
||||
- `hub_private_repo`: None
|
||||
- `hub_always_push`: False
|
||||
- `hub_revision`: None
|
||||
- `gradient_checkpointing`: False
|
||||
- `gradient_checkpointing_kwargs`: None
|
||||
- `include_inputs_for_metrics`: False
|
||||
- `include_for_metrics`: []
|
||||
- `eval_do_concat_batches`: True
|
||||
- `fp16_backend`: auto
|
||||
- `push_to_hub_model_id`: None
|
||||
- `push_to_hub_organization`: None
|
||||
- `mp_parameters`:
|
||||
- `auto_find_batch_size`: False
|
||||
- `full_determinism`: False
|
||||
- `torchdynamo`: None
|
||||
- `ray_scope`: last
|
||||
- `ddp_timeout`: 1800
|
||||
- `torch_compile`: False
|
||||
- `torch_compile_backend`: None
|
||||
- `torch_compile_mode`: None
|
||||
- `include_tokens_per_second`: False
|
||||
- `include_num_input_tokens_seen`: no
|
||||
- `neftune_noise_alpha`: None
|
||||
- `optim_target_modules`: None
|
||||
- `batch_eval_metrics`: False
|
||||
- `eval_on_start`: False
|
||||
- `use_liger_kernel`: False
|
||||
- `liger_kernel_config`: None
|
||||
- `eval_use_gather_object`: False
|
||||
- `average_tokens_across_devices`: True
|
||||
- `prompts`: None
|
||||
- `batch_sampler`: batch_sampler
|
||||
- `multi_dataset_batch_sampler`: round_robin
|
||||
- `router_mapping`: {}
|
||||
- `learning_rate_mapping`: {}
|
||||
|
||||
</details>
|
||||
|
||||
### Training Logs
|
||||
| Epoch | Step | structural-val_cosine_accuracy |
|
||||
|:-----:|:----:|:------------------------------:|
|
||||
| 1.0 | 38 | 0.9950 |
|
||||
| 2.0 | 76 | 0.9967 |
|
||||
|
||||
|
||||
### Framework Versions
|
||||
- Python: 3.12.2
|
||||
- Sentence Transformers: 5.1.2
|
||||
- Transformers: 4.57.1
|
||||
- PyTorch: 2.6.0+cu124
|
||||
- Accelerate: 1.12.0
|
||||
- Datasets: 4.4.1
|
||||
- Tokenizers: 0.22.1
|
||||
|
||||
## Citation
|
||||
|
||||
### BibTeX
|
||||
|
||||
#### Sentence Transformers
|
||||
```bibtex
|
||||
@inproceedings{reimers-2019-sentence-bert,
|
||||
title = "Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks",
|
||||
author = "Reimers, Nils and Gurevych, Iryna",
|
||||
booktitle = "Proceedings of the 2019 Conference on Empirical Methods in Natural Language Processing",
|
||||
month = "11",
|
||||
year = "2019",
|
||||
publisher = "Association for Computational Linguistics",
|
||||
url = "https://arxiv.org/abs/1908.10084",
|
||||
}
|
||||
```
|
||||
|
||||
#### TripletLoss
|
||||
```bibtex
|
||||
@misc{hermans2017defense,
|
||||
title={In Defense of the Triplet Loss for Person Re-Identification},
|
||||
author={Alexander Hermans and Lucas Beyer and Bastian Leibe},
|
||||
year={2017},
|
||||
eprint={1703.07737},
|
||||
archivePrefix={arXiv},
|
||||
primaryClass={cs.CV}
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
## Glossary
|
||||
|
||||
*Clearly define terms in order to be accessible across audiences.*
|
||||
-->
|
||||
|
||||
<!--
|
||||
## Model Card Authors
|
||||
|
||||
*Lists the people who create the model card, providing recognition and accountability for the detailed work that goes into its construction.*
|
||||
-->
|
||||
|
||||
<!--
|
||||
## Model Card Contact
|
||||
|
||||
*Provides a way for people who have updates to the Model Card, suggestions, or questions, to contact the Model Card authors.*
|
||||
-->
|
||||
7
Resources/model/added_tokens.json
Normal file
7
Resources/model/added_tokens.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"<GUID>": 250004,
|
||||
"<HEX>": 250006,
|
||||
"<IP>": 250003,
|
||||
"<NUM>": 250002,
|
||||
"<STR>": 250005
|
||||
}
|
||||
25
Resources/model/config.json
Normal file
25
Resources/model/config.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"architectures": [
|
||||
"BertModel"
|
||||
],
|
||||
"attention_probs_dropout_prob": 0.1,
|
||||
"classifier_dropout": null,
|
||||
"dtype": "float32",
|
||||
"gradient_checkpointing": false,
|
||||
"hidden_act": "gelu",
|
||||
"hidden_dropout_prob": 0.1,
|
||||
"hidden_size": 384,
|
||||
"initializer_range": 0.02,
|
||||
"intermediate_size": 1536,
|
||||
"layer_norm_eps": 1e-12,
|
||||
"max_position_embeddings": 512,
|
||||
"model_type": "bert",
|
||||
"num_attention_heads": 12,
|
||||
"num_hidden_layers": 12,
|
||||
"pad_token_id": 0,
|
||||
"position_embedding_type": "absolute",
|
||||
"transformers_version": "4.57.1",
|
||||
"type_vocab_size": 2,
|
||||
"use_cache": true,
|
||||
"vocab_size": 250007
|
||||
}
|
||||
14
Resources/model/config_sentence_transformers.json
Normal file
14
Resources/model/config_sentence_transformers.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"__version__": {
|
||||
"sentence_transformers": "5.1.2",
|
||||
"transformers": "4.57.1",
|
||||
"pytorch": "2.6.0+cu124"
|
||||
},
|
||||
"model_type": "SentenceTransformer",
|
||||
"prompts": {
|
||||
"query": "",
|
||||
"document": ""
|
||||
},
|
||||
"default_prompt_name": null,
|
||||
"similarity_fn_name": "cosine"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
epoch,steps,accuracy_cosine
|
||||
1.0,38,0.9950000047683716
|
||||
2.0,76,0.996666669845581
|
||||
3.0,114,0.996666669845581
|
||||
|
Reference in New Issue
Block a user