Сеть-шифровальщик или как я от нейросети странного хотел

в 15:47, , рубрики: машинное обучение, метки: , ,

Идея использовать нейросети для шифрования информации витала в голове у меня давно (с конца 2000х). Но, как это водится, то времени не хватало, то желания. Так что пишу это только сейчас (хотя может материал уже и устарел).

Вводная

Суть идеи заключается в том что-б передавать не само зашифрованное сообщение а настроенные веса сети которая сможет данное сообщение воспроизвести будучи активированной кужным ключем.

Описание работы

Предположим у нас есть сообщение «Hello i'm EncryptNN and i'm here». Преобразуем его в пару массивов tc, letters; где tc = массив «таймкодов последовательности» а letters соответсвующие байты сообщения. Дальше учим сеть сопоставлять таймкоды определенным байтам сообщения.

Процесс обмена сообщениями выглядит так:

  1. Отправитель передает тело сообщения
  2. Отправитель передает сообщения (подсказка для генератора последовательностей таймкодов)
  3. Получатель загружает в сеть веса переданные в п.1
  4. Получатель загружает в сеть секрет и получает дешифрованное сообщение

Прототип

Сначала нам понядобятся утилитарные ф-ции преобразования из/в строку:

def toChars(s):
    return np.array([ord(c) for c in s])

def toArr(c):
    r = np.zeros(256);
    r[c] = 1
    return r

def toCharArr(s):
    return np.array([toArr(c) for c in toChars(s)])

def fromChars(arr):
    return ''.join(chr(i) for i in arr)

def fromArr(arr):
    return np.argmax(arr)
    
def fromCharArr(arr):
    return fromChars([fromArr(a) for a in arr])
    
def toTimeCode(i, l = 8):
    val = [int(x) for x in bin(i)[2:]]
    result = [0] * (l - len(val))
    result.extend(val)
    return result
    
def stringToSequence(s):
    tc = []
    letters = []
    for t,c in enumerate(toCharArr(s)):
        tc+=[toTimeCode(t)]
        letters+=[c]
    
    return np.array(tc),np.array(letters)

В текущем примере «секрет» — начало последовательности = 0, шаг = 1.

Далее опишем саму сеточку (я буду использовать Keras для краткости). В текущей реализации работает простейший персептрон. Сесть состоит из 2х слоев: входной слой принимающий таймкод и выходной — отдающий байт исходного сообщения закодированный как «one-hot».

inSize = 8 #выбрано просто под полную ASCII таблицу

model = Sequential()
model.add(Dense(inSize, input_shape = (inSize,),activation="relu"))
model.add(Dense(256))
model.add(Activation("softmax"))

model.compile(loss=keras.losses.categorical_crossentropy, optimizer='sgd')

Теперь нам остались только тренировка и экспорт данных для получателя:

#тренировка
tc,l = stringToSequence(testString)

res = 10
cnt = 0 
epochs = 100

while( res > 0.1):
    cnt+=1
    model.fit(tc,l, epochs=epochs, verbose=False)
    res = model.evaluate(tc,l, verbose=False)
    
    print("Loss: ", res)
    v = model.predict(tc)  
    print(fromCharArr(v))
    
print("epochs = ", cnt*epochs)

#экспорт
model.save_weights("/tmp/test1.data.w")

Для примера — обучение фразе «Hello i'm EncryptNN and i'm here» на моем ноутбуке заняло 22800 эпох.

Выводы

  • Такое кодирование в принципе возможно
  • Для кодирования требуются огромные ресурсы, для раскодирования — нет
  • ИМХО (и тут я очень прошу помощи математиков) невозможно восстановить сообщения при налиции только 1 половины данных
  • Все сообщения которым я смог научить сеть весили одинаково
  • Можно щифровать в одну сеть несколько сообщений с разными секретами (немного не очевидно, но очень просто)

Вопросы и доработки

  • Очевидо что секрет выбран простейшим и его надо переделать
  • Какой предел памяти у такой сети (какой макс объем данных так можно зашифровать)?

Автор: Лунтик

Источник

Поделиться

* - обязательные к заполнению поля