Цветы, муха и хорошо отрепетированное случайное машинное обучение

    В предыдущей серии я пытался сделать из мухи разумное существо. Коротко — не вышло. Муха упорно не хотела учиться.



    Мухой была маленькая простая нейронная сеть, основанная на умножении матриц, сигмоиде и обратном распространении ошибки. Её учение состояло в распознавании фотографий с цветами.

    Напомню, что внутри две сети — первая анализирует кусочки исходного изображения, а вторая работает с матрицей, сложенной из результатов работы первой сетки.

    Обучение проходило кое-как, результата не было. Затем, оставив попытки обучения по уважительным причинам (как то — вечер субботы, ночь и утро воскресенья), я все же думал, что делать дальше. Какие-то возможные решения были намечены в конце первой статьи, с них и продолжил.

    Вариант 1. Еще один промежуточный слой


    Предполагалось, что если в муху добавить нейронов, то муха станет немного умнее. Добавление слоя делается просто — вместо

    nn2 = NN([middleShape, (y.shape[1], middleShape[0]), y.shape])
    

    пишем

    nn2 = NN([middleShape, (40, middleShape[0]), (y.shape[1], 40), y.shape])

    Слой добавился, процесс обучения и расчета стал ощутимо медленнее, но не лучше. Фактически результат вовсе не изменился, поэтому здесь не привожу.

    Вариант 2. Использовать приближение к успеху как заготовку


    Идея была запоминать веса синапсов самого успешного результата обучения, и обучать сеть с этими весами, а не забивать случайными значениями, как обычно.

    Синапсы в моей сетке — это содержимое атрибутов nn.net.syns и nn2.syns для первой и второй сеток соответственно. Результат оценивался количеством сделанных ошибок, т.е. числом неправильно классифицированных фотографий по всей выборке, включающей и цветы, и не цветы.

            minFails = None
            lastSyns = None
            for epoch in range(100):
                nn = ImgNN(firstShape, resultShape=middleShape, imageSize=imageSize)
                nn2 = NN([middleShape, (y.shape[1], middleShape[0]), y.shape])
    # цикл обучения
                for f in fl:
                    i = readImage(f, imageSize)
                    nn.learn(i, yy, 2)
                    mid = nn.calc(i)
                    nn2.learn(mid, y, 1000)
                nextSyns=None
                fails = 0
                failFiles = []
    # цикл проверки результата
                for f in all:
                    i = readImage(f, imageSize)
                    mid = nn.calc(i)
                    res = nn2.calc(mid)
                    delta = (y-res)
                    v = round(np.std(delta),3)
                    if v > 0.2 and f in fl: # неправильно - цветок отнесен к не-цветкам
                        fails += 1
                        failFiles.append(f)
                    elif v<0.2 and f in nofl: # неправильно - не-цветок отнесен к цветкам
                        fails +=1
                        failFiles.append(f)
    

    Когда ошибок становится меньше, то текущее состояние сетки фиксирую, передаю на инициализацию:

                nn = ImgNN(firstShape, resultShape=middleShape, imageSize=imageSize)
                if not (lastSyns is None):
                    nn.net.syns = lastSyns
                nn2 = NN([middleShape, (y.shape[1], middleShape[0]), y.shape])
    # цикл обучения
    # цикл проверки результата
                if minFails == None or fails < minFails:
                    minFails = fails
                    lastSyns = nn.net.syns
    

    Проверяем

    Лучший результат: 4 ошибки на попытке 25
    Epoch = 25
    flowers\178.jpg res = [[ 0.64 0.89 0.65 0.87]] v = 0.619
    flowers\179.jpg res = [[ 0.91 0.2 0.96 0.15]] v = 0.12
    flowers\180.jpg res = [[ 0.95 0.1 0.95 0.1 ]] v = 0.074
    flowers\182.jpg res = [[ 1. 0. 1. 0.]] v = 0.0
    flowers\186-2.jpg res = [[ 0.98 0.05 0.98 0.04]] v = 0.032
    flowers\186.jpg res = [[ 0.99 0.01 0.99 0.01]] v = 0.01
    flowers\187.jpg res = [[ 0.83 0.48 0.81 0.5 ]] v = 0.335
    flowers\190 (2).jpg res = [[ 1. 0. 1. 0.]] v = 0.001
    flowers\190.jpg res = [[ 0.96 0.06 0.96 0.05]] v = 0.045
    flowers\191.jpg res = [[ 0.97 0.01 0.96 0.01]] v = 0.022
    flowers\195.jpg res = [[ 1. 0. 1. 0.]] v = 0.004
    flowers\199.jpg res = [[ 0.91 0.16 0.9 0.16]] v = 0.127
    flowers\2.jpg res = [[ 0.99 0.01 0.99 0.01]] v = 0.009
    flowers\200.jpg res = [[ 0.99 0.01 1. 0.01]] v = 0.009
    noflowers\032.jpg res = [[ 0.71 0.73 0.79 0.73]] v = 0.49
    noflowers\085.jpg res = [[ 0.87 0.29 0.85 0.32]] v = 0.222
    noflowers\088.jpg res = [[ 0.92 0.22 0.94 0.24]] v = 0.15
    noflowers\122.JPG res = [[ 0.72 0.68 0.73 0.68]] v = 0.479
    noflowers\123.jpg res = [[ 0.74 0.54 0.69 0.6 ]] v = 0.427
    noflowers\173.jpg res = [[ 0.43 0.9 0.57 0.9 ]] v = 0.702
    noflowers\202.jpg res = [[ 0.99 0. 0.98 0. ]] v = 0.008
    noflowers\205.jpg res = [[ 0.34 0.92 0.57 0.81]] v = 0.711
    noflowers\cutxml.jpg res = [[ 0.79 0.41 0.79 0.41]] v = 0.309
    noflowers\Getaway.jpg res = [[ 0.75 0.65 0.76 0.65]] v = 0.449
    noflowers\IMGP1800.JPG res = [[ 0.81 0.55 0.81 0.55]] v = 0.367
    noflowers\trq-4.png res = [[ 0.52 0.81 0.54 0.83]] v = 0.644
    dy = 1.407 dn = 4.958
    fails = 4 ['flowers\\178.jpg', 'flowers\\187.jpg', 'noflowers\\088.jpg', 'noflowers\\202.jpg']
    min = 4


    Сеть опять ничему не научилась. Что за глупая муха!

    Хотя на самом деле сеть все-таки кое-чему научилась. Если посмотреть на отбракованные картинки, то видно, что сеть отнесла к цветам битую красную машину, усыпанную желтыми листьями. При некотором воображении ее действительно можно принять за цветок.



    Вариант 3. И чему вас там только учат?


    Следующей точкой воздействия стала цель обучения первой сети. Дело в том, что накопленную ошибку на входах второй сети не передать через выходы первой. Ну, как не передать… можно передать, но это еще программировать надо. И я поленился и стал обучать первую сеть на единичную матрицу:

            yy = np.zeros(middleShape)
            np.fill_diagonal(yy,1)
    

    \begin{bmatrix}
    1 & 0 & \cdots & 0\\
    0 & 1 & \cdots & 0\\
    \cdots & \cdots & \cdots & \cdots \\
    0 & 0 &\cdots & 1
    \end{bmatrix}
    Выглядит это несколько искусственно, видимо, и в обучении мешает. Но какой должна быть матрица результата, я не знал. Игры с копированием синапсов навели меня на мысль, что неплохо бы целевую матрицу обучения $yy$ тоже копировать из успешной попытки, так постепенно приближаясь к наилучшему результату.

    Однако была проблема — у меня не было одного образца. Первую сеть я обучаю на единичной матрице, давая на вход различные картинки. Для каждой из них дает некую матрицу значений на выходе. Какую же из полученных матриц я должен взять за образец?

    Зная, что «настоящие герои всегда идут в обход», я просто не стал так делать, а взял для образца ту самую единичную матрицу. Но только добавил к ней небольшую случайную ошибку. И, если цикл был удачным, т.е. мы достигли следующего минимума ошибок, то последний образец будет следующим целевым результатом.

            yy = np.zeros(middleShape)
            np.fill_diagonal(yy,1)
            minFails = None
            lastYY = yy
            nextYY = yy
    ...
            for epoch in range(100):
    ...
                for f in fl:
                    i = readImage(f, imageSize)
                    nn.learn(i, nextYY, 2)
                    mid = nn.calc(i)
                    nn2.learn(mid, y, 1000)
    ...
                for f in all:
                    i = readImage(f, imageSize)
                    mid = nn.calc(i)
                    res = nn2.calc(mid)
    ...
                if minFails == None or fails < minFails:
                    minFails = fails
                    lastYY = nextYY
                else:
                    nextYY = lastYY +(np.random.random(yy.shape)-0.5)/10
    


    Наверное, будь я математиком, вряд ли бы я стал использовать случайность в качестве инструмента обучения. Математик построил бы теорию, по которой обучение неизбежно приводит к ожидаемому результате через $N$ ходов, нужно только посчитать интеграл по криволинейной поверхности в многомерном пространстве и взять от него производную по $x$. Но я не математик и могу только опираться на мое нематематическое ожидание, что решение где-то есть и оно рядом.

    Лучший результат: 4 ошибки на попытке 79
    Epoch = 79
    flowers\178.jpg res = [[ 0.5 0.13 0.52 0.12]] v = 0.309
    flowers\179.jpg res = [[ 0.74 0.06 0.75 0.06]] v = 0.16
    flowers\180.jpg res = [[ 0.76 0.07 0.75 0.07]] v = 0.155
    flowers\182.jpg res = [[ 0.95 0.03 0.94 0.03]] v = 0.044
    flowers\186-2.jpg res = [[ 0.7 0.1 0.71 0.09]] v = 0.193
    flowers\186.jpg res = [[ 0.61 0.22 0.6 0.2 ]] v = 0.303
    flowers\187.jpg res = [[ 0.45 0.13 0.45 0.13]] v = 0.341
    flowers\190 (2).jpg res = [[ 0.84 0. 0.67 0.01]] v = 0.14
    flowers\190.jpg res = [[ 0.96 0.06 0.94 0.08]] v = 0.061
    flowers\191.jpg res = [[ 0.73 0.13 0.72 0.1 ]] v = 0.194
    flowers\195.jpg res = [[ 0.85 0.03 0.88 0.03]] v = 0.08
    flowers\199.jpg res = [[ 0.83 0.05 0.84 0.04]] v = 0.102
    flowers\2.jpg res = [[ 0.81 0.06 0.81 0.06]] v = 0.125
    flowers\200.jpg res = [[ 0.92 0.05 0.93 0.04]] v = 0.057
    noflowers\032.jpg res = [[ 0.27 0.12 0.3 0.1 ]] v = 0.416
    noflowers\085.jpg res = [[ 0.41 0.14 0.41 0.14]] v = 0.365
    noflowers\088.jpg res = [[ 0.37 0.15 0.32 0.15]] v = 0.402
    noflowers\122.JPG res = [[ 0.4 0.15 0.4 0.14]] v = 0.373
    noflowers\123.jpg res = [[ 0.35 0.14 0.33 0.15]] v = 0.401
    noflowers\173.jpg res = [[ 0.33 0.17 0.34 0.17]] v = 0.418
    noflowers\202.jpg res = [[ 0.44 0.14 0.45 0.12]] v = 0.342
    noflowers\205.jpg res = [[ 0.63 0.06 0.74 0.07]] v = 0.192
    noflowers\cutxml.jpg res = [[ 0.52 0.13 0.45 0.13]] v = 0.323
    noflowers\Getaway.jpg res = [[ 0.38 0.15 0.38 0.15]] v = 0.386
    noflowers\IMGP1800.JPG res = [[ 0.4 0.15 0.4 0.14]] v = 0.371
    noflowers\trq-4.png res = [[ 0.19 0.21 0.17 0.28]] v = 0.533
    dy = 2.264 dn = 4.522
    fails = 4 ['flowers\\178.jpg', 'flowers\\186.jpg', 'flowers\\187.jpg', 'noflowers\\205.jpg']
    min = 4


    Здесь сеть посчитала, что белый разъем тоже вполне выглядит, как цветок.



    Вообще этот разъем сеть практически в любом варианте считала цветком. Ну, это понятно, понюхать-то ей не дают.

    Вариант 4. А давайте объединим наши бренды


    Предыдущие варианты показали примерно одинаковые результаты. К этому моменту я успел подумать, то ли я вообще делаю и не слишком ли много я хочу от несчастной мухи. Но не остановился на этом.

    Следующим вариантом было использование предыдущих двух одновременно — при успешных попытках запоминать значения синапсов И цель обучения первой матрицы И использовать случайность для расширения поиска.

            for epoch in range(100):
                print('Epoch =', epoch)
                nn = ImgNN(firstShape, resultShape=middleShape, imageSize=imageSize)
                if not (lastSyns is None):
                    nextSyns = lastSyns
                    for r in range(len(nextSyns)):
                        rand = (np.random.random(nextSyns[r].shape)-0.5)/20
                        nextSyns[r] = nextSyns[r] + rand
                    nn.net.syns = nextSyns
                nn2 = NN([middleShape, (y.shape[1], middleShape[0]), y.shape])
                for f in fl:
                    i = readImage(f, imageSize)
                    nn.learn(i, nextYY, 2)
                    mid = nn.calc(i)
                    nn2.learn(mid, y, 1000)
    ...
                if minFails == None or fails < minFails:
                    minFails = fails
                    lastSyns = nn.net.syns
                    lastYY = nextYY
                else:
                    nextYY = lastYY +(np.random.random(yy.shape)-0.5)/20
    

    Лучший результат: 2 ошибки на попытке 38
    Epoch = 38
    flowers\178.jpg res = [[ 0.91 0.26 0.91 0.25]] v = 0.174
    flowers\179.jpg res = [[ 0.99 0. 0.99 0. ]] v = 0.005
    flowers\180.jpg res = [[ 0.9 0.21 0.89 0.2 ]] v = 0.153
    flowers\182.jpg res = [[ 1. 0. 1. 0.]] v = 0.0
    flowers\186-2.jpg res = [[ 1. 0.01 0.99 0.01]] v = 0.008
    flowers\186.jpg res = [[ 0.91 0.12 0.93 0.07]] v = 0.09
    flowers\187.jpg res = [[ 0.83 0.43 0.83 0.44]] v = 0.303
    flowers\190 (2).jpg res = [[ 1. 0. 1. 0.]] v = 0.0
    flowers\190.jpg res = [[ 1. 0. 1. 0.]] v = 0.001
    flowers\191.jpg res = [[ 1. 0. 1. 0.]] v = 0.0
    flowers\195.jpg res = [[ 0.99 0. 1. 0. ]] v = 0.004
    flowers\199.jpg res = [[ 0.97 0.03 0.98 0.03]] v = 0.029
    flowers\2.jpg res = [[ 1. 0. 1. 0.]] v = 0.003
    flowers\200.jpg res = [[ 1. 0. 1. 0.]] v = 0.0
    noflowers\032.jpg res = [[ 0.88 0.55 0.8 0.67]] v = 0.389
    noflowers\085.jpg res = [[ 0.25 0.96 0.27 0.96]] v = 0.848
    noflowers\088.jpg res = [[ 0.84 0.42 0.79 0.37]] v = 0.29
    noflowers\122.JPG res = [[ 0.68 0.66 0.69 0.66]] v = 0.49
    noflowers\123.jpg res = [[ 0.74 0.63 0.71 0.6 ]] v = 0.445
    noflowers\173.jpg res = [[ 0.86 0.46 0.76 0.52]] v = 0.343
    noflowers\202.jpg res = [[ 0.22 0.92 0.44 0.95]] v = 0.808
    noflowers\205.jpg res = [[ 0.8 0.82 0.71 0.88]] v = 0.547
    noflowers\cutxml.jpg res = [[ 0.99 0.03 0.97 0.02]] v = 0.022
    noflowers\Getaway.jpg res = [[ 0.7 0.65 0.7 0.65]] v = 0.474
    noflowers\IMGP1800.JPG res = [[ 0.79 0.5 0.77 0.5 ]] v = 0.36
    noflowers\trq-4.png res = [[ 0.77 0.21 0.69 0.07]] v = 0.215
    dy = 0.77 dn = 5.231
    fails = 2 ['flowers\\187.jpg', 'noflowers\\cutxml.jpg']
    min = 2


    2 ошибки на 28 файлов = 7% я посчитал достаточно хорошим результатом, который можно и не улучшать.

    Прогон по конечным фотографиям и выводы


    В конце процесса я сохранил обученную сетку, а точнее значения ее синапсов для лучшего результата, в текстовых файлах:

            for i in range(len(lastSyns)):
                np.savetxt('syns_save%s.txt'%i, lastSyns[i])
            for i in range(len(lastSyns2)):
                np.savetxt('syns2_save%s.txt'%i, lastSyns2[i])
    

    Потом прогнал сеть по фотографиям:

        StartLearn = False
    
        if not StartLearn:
            pictDir = 'C:\\AllPictures'
            nn = ImgNN(firstShape, resultShape=middleShape, imageSize=imageSize)
            nn.net.syns[0] = np.loadtxt('syns_save0.txt',ndmin=nn.net.syns[0].ndim)
            nn.net.syns[1] = np.loadtxt('syns_save1.txt',ndmin=nn.net.syns[1].ndim)
            nn2 = NN([middleShape, (y.shape[1], middleShape[0]), y.shape])
            nn2.syns[0] = np.loadtxt('syns2_save0.txt',ndmin=nn2.syns[0].ndim)
            nn2.syns[1] = np.loadtxt('syns2_save1.txt',ndmin=nn2.syns[1].ndim)
            files = [e.path for e in os.scandir(pictDir)]
            for f in files:
                i = readImage(f, imageSize)
                mid = nn.calc(i)
                res = nn2.calc(mid)
                delta = y-res
                v = round(np.std(delta),3)
                if v <= 0.3:
                    print('Flower',f,v)
    ##            else:
    ##                print('No flower',f, v)
    

    В общем, не сказать, что она нашла только цветы. Попадались кошки, крабы, фейверки, просто камни и группы людей. А цветов среди моих фотографий было просто мало.

    Можно сделать вывод, что в принципе сеть работает, просто задача для нее оказалась слишком сложной. Если посмотреть на ImageNet, как мне посоветовал Dark_Daiver в комментарии к прошлой статье, то у цветов слишком большое разнообразие, чтобы суметь отличать их такой простой сетью.

    Исходный код
    import numpy as np
    from nnmat import *
    import os
    
    import sys
    from PyQt5.QtGui import *
    from PyQt5.QtCore import *
    import meshandler
     
    import random
    import cv2
    
    class ImgNN:
        def __init__(self, shape, resultShape = (16, 16), imageSize = (400,400)):
            self.resultShape = resultShape
            self.w = imageSize[0] // shape[0]
            self.h = imageSize[1] // shape[1]
            self.net = NN([shape, (1,shape[0]), (1,1)])
            self.shape = shape
            self.imageSize = imageSize
    
        def learn(self, srcArr, result, cycles):
            for c in range(cycles):
                for x in range(self.w):
                    for y in range(self.h):
                        a = srcArr[x:x+self.shape[0], y:y+self.shape[1]]
                        if a.shape != (self.shape[0], self.shape[1]):
                            print(a.shape)
                            continue
                        self.net.learn(a, result[x,y], 1)
    
        def calc(self, srcArr):
            resArr = np.zeros(self.resultShape)
            for x in range(self.w):
                for y in range(self.h):
                    a = srcArr[x:x+self.shape[0], y:y+self.shape[1]]
                    if a.shape != (self.shape[0], self.shape[1]):
                        continue
                    if x >= self.resultShape[0] or y >= self.resultShape[1]:
                        continue
                    res = self.net.calc(a)
                    resArr[x,y] = res[0,0]
            return resArr
            
        def learnFile(self, file, result, cycles):
            return self.learn(readImage(file, self.imageSize), result, cycles)
    
        def calcFile(self, file):
            return self.calc(readImage(file, self.imageSize))
    
    def readImageCV(file, imageSize):
        img = cv2.imread(file)
        small = cv2.resize(img, imageSize)
        hsv = cv2.cvtColor(small, cv2.COLOR_BGR2HSV)
        return hsv[:,:,0]/255
    
    def readImageQ(file, imageSize):
        img = QImage(file)
        if img.isNull():
            return 0
        img = img.convertToFormat(QImage.Format_Grayscale8)
        img = img.scaled(imageSize[0],imageSize[1],Qt.IgnoreAspectRatio)
        srcBi = img.bits()
        srcBi.setsize(img.width() * img.height())
        srcBy = bytes(srcBi)
    
        srcW, srcH = img.width(), img.height()
        srcArr = np.recarray((srcH, srcW), dtype=np.uint8, buf=srcBy).view(dtype=np.uint8,type=np.ndarray)
        return srcArr/255
    
    def readImageCVQ(file, imageSize):
        img = QImage(file)
        if img.isNull():
            return 0
        img = img.convertToFormat(QImage.Format_RGB888)
        img = img.scaled(imageSize[0],imageSize[1],Qt.IgnoreAspectRatio)
        srcBi = img.bits()
        srcBi.setsize(img.byteCount())
        srcBy = bytes(srcBi)
    
        srcW, srcH = img.width(), img.height()
        bp = img.depth() // 8
        srcArr = np.recarray((srcH, srcW, bp), dtype=np.uint8, buf=srcBy)
        srcArr = srcArr.view(dtype=np.uint8,type=np.ndarray)
        hsv = cv2.cvtColor(srcArr, cv2.COLOR_RGB2HSV)
        return hsv[:,:,0]/255
    
    
    if __name__ == '__main__':
    
        readImage = readImageCVQ
    
        y = np.array([[1,0,1,0]])
        firstShape = (40, 40)
        middleShape = (10, 10)
        imageSize = firstShape[0]*middleShape[0], firstShape[1]*middleShape[1]
        
    
        StartLearn = False
    
        if not StartLearn:
            pictDir = 'C:\\AllPictures'
            nn = ImgNN(firstShape, resultShape=middleShape, imageSize=imageSize)
            nn.net.syns[0] = np.loadtxt('syns_save0.txt',ndmin=nn.net.syns[0].ndim)
            nn.net.syns[1] = np.loadtxt('syns_save1.txt',ndmin=nn.net.syns[1].ndim)
            nn2 = NN([middleShape, (y.shape[1], middleShape[0]), y.shape])
            nn2.syns[0] = np.loadtxt('syns2_save0.txt',ndmin=nn2.syns[0].ndim)
            nn2.syns[1] = np.loadtxt('syns2_save1.txt',ndmin=nn2.syns[1].ndim)
            files = [e.path for e in os.scandir(pictDir)]
            for f in files:
                i = readImage(f, imageSize)
                mid = nn.calc(i)
                res = nn2.calc(mid)
                delta = y-res
                v = round(np.std(delta),3)
                if v <= 0.3:
                    print('Flower',f,v)
    ##            else:
    ##                print('No flower',f, v)
        else:    
            fl = [e.path for e in os.scandir('flowers')]
            nofl = [e.path for e in os.scandir('noflowers')]
            all = fl+nofl
            yy = np.zeros(middleShape)
            np.fill_diagonal(yy,1)
            minFails = None
            lastSyns = None
            nextSyns = None
            lastSyns2 = None
            lastYY = yy
            nextYY = yy
            minDy = None
            maxDn = None
            for epoch in range(100):
                print('Epoch =', epoch)
                nn = ImgNN(firstShape, resultShape=middleShape, imageSize=imageSize)
                if not (lastSyns is None):
                    nextSyns = lastSyns
                    for r in range(len(nextSyns)):
                        rand = (np.random.random(nextSyns[r].shape)-0.5)/20
                        nextSyns[r] = nextSyns[r] + rand
                    nn.net.syns = nextSyns
                nn2 = NN([middleShape, (y.shape[1], middleShape[0]), y.shape])
                for f in fl:
                    i = readImage(f, imageSize)
                    nn.learn(i, nextYY, 2)
    ##                nn.learn(i, yy, 2)
                    mid = nn.calc(i)
                    nn2.learn(mid, y, 1000)
                nextSyns=None
                fails = 0
                failFiles = []
                dy = 0.0
                dn = 0.0
                for f in all:
                    i = readImage(f, imageSize)
                    mid = nn.calc(i)
                    res = nn2.calc(mid)
                    delta = (y-res)
                    v = round(np.std(delta),3)
                    #v = round(delta.sum(),3)
                    print(f, 'res = ', res.round(2),'v =',v)
                    if f in fl:
                        dy += v
                    if f in nofl:
                        dn += v
                    if v > 0.2 and f in fl:
                        fails += 1
                        failFiles.append(f)
                    elif v<0.2 and f in nofl:
                        fails +=1
                        failFiles.append(f)
                print('dy =',dy,'dn =',dn)
                if minDy == None or dy < minDy:
                    minDy = dy
                if maxDn == None or dn > maxDn:
                    maxDn = dn
                if minFails == None or fails < minFails:
                    minFails = fails
                    lastSyns = nn.net.syns
                    lastSyns2 = nn2.syns
                    lastYY = nextYY
                else:
                    nextYY = lastYY +(np.random.random(yy.shape)-0.5)/20
                print('fails =',fails, failFiles)
                print('min =',minFails)
                if minFails <= 1:
                    print('found!')
                    break
            for i in range(len(lastSyns)):
                np.savetxt('syns_save%s.txt'%i, lastSyns[i])
            for i in range(len(lastSyns2)):
                np.savetxt('syns2_save%s.txt'%i, lastSyns2[i])
    


    Исходный код также выложен на github под лицензией MIT.

    Бонус. OpenCV и русские буквы в путях файлов


    В прошлой статье я говорил, что у функции imread в OpenCV была проблема со чтением файлов с путями, в которых встречаются русские буквы. Такой проблемы нет у QImage из PyQt, но OpenCV мне был нужен для конвертации картинки в цветовое пространство HSV и для выделения плоскости цветности. Поэтому я совместил загрузку картинки через QImage и преобразование через OpenCV.

    def readImageCVQ(file, imageSize):
        img = QImage(file)
        if img.isNull():
            return 0
        img = img.convertToFormat(QImage.Format_RGB888)
        img = img.scaled(imageSize[0],imageSize[1],Qt.IgnoreAspectRatio)
        srcBi = img.bits()
        srcBi.setsize(img.byteCount())
        srcBy = bytes(srcBi)
    
        srcW, srcH = img.width(), img.height()
        bp = img.depth() // 8
        srcArr = np.recarray((srcH, srcW, bp), dtype=np.uint8, buf=srcBy)
        srcArr = srcArr.view(dtype=np.uint8,type=np.ndarray)
        hsv = cv2.cvtColor(srcArr, cv2.COLOR_RGB2HSV)
        return hsv[:,:,0]/255
    ...
    
        readImage = readImageCVQ
    
    Share post

    Comments 6

      +2

      Вы пытаетесь решать проблему интуитивным подбором. Зачем? Есть же документация и примеры, как решать такие задачи. Вы хотите переизобрести заново? Можно, но таком случае не видно научного подхода.

        +2

        В начале первой статьи автор ясно написал: "мне захотелось поиграться с как бы «нейронной» сетью". Обычная игра — попробуй решить задачу, полагаясь исключительно на себя. Получится криво, косо, велосипедно, всегда найдётся туча готовых решений… Ну и что? Делается-то это не ради того, что бы отделить цветы от не-цветов, а просто ради фана и какого-то первоначального понимания области знаний.

          +1

          Да, вы всё правильно описали. Just for fun.

          +1

          Я очень надеялся избежать какого-либо научного подхода, потому что в моем случае он был бы наукообразным...

          0
          Простите, мне не совсем понятно, как вы свертку изображений сделали. Можете расписать подробнее? Я спрашиваю, поскольку полагаю, что именно от её качества и зависит дальнейшая классификация. Если свертка объектов приводит к тому, что машина и цветок будут одинаковыми объектами, то как сеть сможет решить проблему их классификации?
            0

            Принцип "свертки" описан в первой статье, ссылка на нее в начале. В кавычках, потому что она не совсем честная. В классической свертки ядро движется по пикселу исходного изображения за шаг, а я разбил изображение на кусочки, равные размеру ядра, и обработал их один раз.

          Only users with full accounts can post comments. Log in, please.