Организация дополнительного потока для тяжелых расчетов

В программе по аппроксимации функции нейронной сетью, нейронная сеть медленно аппроксимирует исходную функцию(то есть тренируется и предсказывает значения функции) даже на моем ноутбуке с процессором intel core i5 9300h, выражается это в медленном рендеринге графика в форме scatter, мне как-то советовали организовать доп. поток для тяжелых расчётов, чтобы процесс аппроксимации был быстрее, но я не знаю как это сделать в рамках данной программы, код:

# -*- coding: utf-8 -*-

    # Form implementation generated from reading ui file 'funcAprox.ui'
    #
    # Created by: PyQt5 UI code generator 5.15.4
    #
    # WARNING: Any manual changes made to this file will be lost when pyuic5 is
    # run again.  Do not edit this file unless you know what you are doing.
    
    
    from PyQt5 import QtCore, QtGui, QtWidgets
    from PyQt5.Qt import *
    import numpy as np
    from sympy import *
    from pyqtgraph import PlotWidget
    import pyqtgraph as pg     
    
    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName("MainWindow")
            MainWindow.resize(1778, 783)
            self.centralwidget = QtWidgets.QWidget(MainWindow)
            self.centralwidget.setStyleSheet("background-color: rgb(10, 170, 0);\n"
    "background-color: rgb(73, 152, 255);")
            self.centralwidget.setObjectName("centralwidget")
            self.graphicsView = PlotWidget(self.centralwidget)
            self.graphicsView.setGeometry(QtCore.QRect(450, 30, 621, 631))
            self.graphicsView.setObjectName("graphicsView")
            self.graphicsView.setBackgroundBrush(QColor("white"))
            self.pushButton = QtWidgets.QPushButton(self.centralwidget)
            self.pushButton.setGeometry(QtCore.QRect(20, 460, 341, 41))
            font = QtGui.QFont()
            font.setPointSize(10)
            font.setBold(True)
            font.setWeight(75)
            self.pushButton.setFont(font)
            self.pushButton.setStyleSheet("background: rgb(90, 211, 255)")
            self.pushButton.setObjectName("pushButton")
            self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
            self.pushButton_2.setGeometry(QtCore.QRect(20, 580, 341, 41))
            font = QtGui.QFont()
            font.setPointSize(10)
            font.setBold(True)
            font.setWeight(75)
            self.pushButton_2.setFont(font)
            self.pushButton_2.setStyleSheet("background: rgb(90, 211, 255)")
            self.pushButton_2.setObjectName("pushButton_2")
            self.label_4 = QtWidgets.QLabel(self.centralwidget)
            self.label_4.setGeometry(QtCore.QRect(20, 240, 331, 21))
            font = QtGui.QFont()
            font.setPointSize(10)
            font.setBold(True)
            font.setWeight(75)
            self.label_4.setFont(font)
            self.label_4.setObjectName("label_4")
            self.label_5 = QtWidgets.QLabel(self.centralwidget)
            self.label_5.setGeometry(QtCore.QRect(20, 310, 411, 21))
            font = QtGui.QFont()
            font.setPointSize(10)
            font.setBold(True)
            font.setWeight(75)
            self.label_5.setFont(font)
            self.label_5.setObjectName("label_5")
            #Вид функции 
            self.textEdit_6 = QtWidgets.QTextEdit(self.centralwidget)
            self.textEdit_6.setGeometry(QtCore.QRect(20, 410, 221, 31))
            self.textEdit_6.setStyleSheet("background: rgb(255, 255, 255)")
            self.textEdit_6.setObjectName("textEdit_6")
            self.textEdit_6.setPlainText("sin(x)*10 + x**2")
            self.label_6 = QtWidgets.QLabel(self.centralwidget)
            self.label_6.setGeometry(QtCore.QRect(20, 380, 391, 21))
            font = QtGui.QFont()
            font.setPointSize(10)
            font.setBold(True)
            font.setWeight(75)
            self.label_6.setFont(font)
            self.label_6.setObjectName("label_6")
            #Кол-во эпох
            self.textEdit_7 = QtWidgets.QTextEdit(self.centralwidget)
            self.textEdit_7.setGeometry(QtCore.QRect(20, 340, 221, 31))
            self.textEdit_7.setStyleSheet("background: rgb(255, 255, 255)")
            self.textEdit_7.setObjectName("textEdit_7")
            self.textEdit_7.setPlainText("100")        
            #Скорость обучения
            self.textEdit_8 = QtWidgets.QTextEdit(self.centralwidget)
            self.textEdit_8.setGeometry(QtCore.QRect(20, 270, 221, 31))
            self.textEdit_8.setStyleSheet("background: rgb(255, 255, 255)")
            self.textEdit_8.setObjectName("textEdit_8")
            self.textEdit_8.setPlainText("0.0001")
            
            self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)
            self.pushButton_3.setGeometry(QtCore.QRect(20, 520, 341, 41))
            font = QtGui.QFont()
            font.setPointSize(10)
            font.setBold(True)
            font.setWeight(75)
            self.pushButton_3.setFont(font)
            self.pushButton_3.setStyleSheet("background: rgb(90, 211, 255)")
            self.pushButton_3.setObjectName("pushButton_3")
            #Объём выборочных данных 
            self.textEdit_11 = QtWidgets.QTextEdit(self.centralwidget)
            self.textEdit_11.setGeometry(QtCore.QRect(20, 130, 221, 31))
            self.textEdit_11.setStyleSheet("background: rgb(255, 255, 255)")
            self.textEdit_11.setObjectName("textEdit_11")
            self.textEdit_11.setPlainText("100")
            
            self.label = QtWidgets.QLabel(self.centralwidget)
            self.label.setGeometry(QtCore.QRect(20, 100, 291, 21))
            font = QtGui.QFont()
            font.setPointSize(10)
            font.setBold(True)
            font.setWeight(75)
            self.label.setFont(font)
            self.label.setObjectName("label")
            self.label_2 = QtWidgets.QLabel(self.centralwidget)
            self.label_2.setGeometry(QtCore.QRect(20, 170, 351, 21))
            font = QtGui.QFont()
            font.setPointSize(10)
            font.setBold(True)
            font.setWeight(75)
            self.label_2.setFont(font)
            self.label_2.setObjectName("label_2")
            #Скрытый слой
            self.textEdit_10 = QtWidgets.QTextEdit(self.centralwidget)
            self.textEdit_10.setGeometry(QtCore.QRect(20, 200, 221, 31))
            self.textEdit_10.setStyleSheet("background: rgb(255, 255, 255)")
            self.textEdit_10.setObjectName("textEdit_10")
            self.textEdit_10.setPlainText("50")
            self.label_3 = QtWidgets.QLabel(self.centralwidget)
            self.label_3.setGeometry(QtCore.QRect(10, 20, 421, 71))
            font = QtGui.QFont()
            font.setPointSize(7)
            font.setBold(True)
            font.setWeight(75)
            self.label_3.setFont(font)
            self.label_3.setObjectName("label_3")
            self.graphicsView_2 = PlotWidget(self.centralwidget)
            self.graphicsView_2.setGeometry(QtCore.QRect(1080, 30, 621, 631))
            self.graphicsView_2.setObjectName("graphicsView_2")
            self.graphicsView_2.setBackgroundBrush(QColor("white"))
            MainWindow.setCentralWidget(self.centralwidget)
            self.statusbar = QtWidgets.QStatusBar(MainWindow)
            self.statusbar.setObjectName("statusbar")
            MainWindow.setStatusBar(self.statusbar)
    
            self.retranslateUi(MainWindow)
            QtCore.QMetaObject.connectSlotsByName(MainWindow)
    
        def retranslateUi(self, MainWindow):
            _translate = QtCore.QCoreApplication.translate
            MainWindow.setWindowTitle(_translate("MainWindow", "Аппроксимация функции нейронной сетью"))
            self.pushButton.setText(_translate("MainWindow", "Построить аппроксимацию функции"))
            self.pushButton_2.setText(_translate("MainWindow", "Очистить графич.поверхность"))
            self.label_4.setText(_translate("MainWindow", "Скорость обучения(learning rate)"))
            self.label_5.setText(_translate("MainWindow", "Количество эпох обучения(number of epoch)"))
            self.label_6.setText(_translate("MainWindow", "Задайте вид функции f(x):"))
            self.pushButton_3.setText(_translate("MainWindow", "Остановить процесс"))
            self.label.setText(_translate("MainWindow", "Объём выборочных данных"))
            self.label_2.setText(_translate("MainWindow", "Количество нейронов в скрытом слое"))
            self.label_3.setText(_translate("MainWindow", "*Количество нейронов во входном слое и выходном всегда 1"))
    
    #Модифицированная нейросеть
    class PartyNN(object):
        
        def __init__(self, learning_rate=0.1, input_nodes=1, hidden_nodes=5, output_nodes=1):
            input_nodes+=1
            self.weights_0_1 = np.random.normal(0.0, hidden_nodes ** -0.5, (hidden_nodes, input_nodes))
            self.weights_1_2 = np.random.normal(0.0, output_nodes ** -0.5, (output_nodes, hidden_nodes))
            self.sigmoid_mapper = np.vectorize(self.sigmoid)
            self.learning_rate = np.array([learning_rate])
            
        def set_lr(lr):
            self.learning_rate = np.array([lr])
            
        def sigmoid(self, x):
            return 1 / (1 + np.exp(-x))
        
        def predict(self, inputs):
            inputs = np.concatenate((inputs,[1]))
            inputs_1 = np.dot(self.weights_0_1, inputs)
            outputs_1 = self.sigmoid_mapper(inputs_1)
            
            inputs_2 = np.dot(self.weights_1_2, outputs_1)
            #outputs_2 = self.sigmoid_mapper(inputs_2)
            outputs_2 = inputs_2
            return outputs_2
        
        def train(self, inputs, expected_predict):
            inputs = np.concatenate((inputs,[1]))        
            inputs_1 = np.dot(self.weights_0_1, inputs)
            outputs_1 = self.sigmoid_mapper(inputs_1)
            
            inputs_2 = np.dot(self.weights_1_2, outputs_1)
            #outputs_2 = self.sigmoid_mapper(inputs_2)
            outputs_2 = inputs_2
            actual_predict = outputs_2[0]
            
            error_layer_2 = np.array([actual_predict - expected_predict])
            gradient_layer_2 = 1 # Здесь что-то я не так сделал. Нужно учитывать вес# actual_predict * (1 - actual_predict)
            weights_delta_layer_2 = error_layer_2 * gradient_layer_2  
            self.weights_1_2 -= (np.dot(weights_delta_layer_2, outputs_1.reshape(1, len(outputs_1)))) * self.learning_rate
            
            error_layer_1 = weights_delta_layer_2 * self.weights_1_2
            gradient_layer_1 = outputs_1 * (1 - outputs_1)
            weights_delta_layer_1 = error_layer_1 * gradient_layer_1
            self.weights_0_1 -= np.dot(inputs.reshape(len(inputs), 1), weights_delta_layer_1).T  * self.learning_rate
    
    
    class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
        def __init__(self):
            super().__init__()
            self.setupUi(self)
            self.pushButton.clicked.connect(self.start_func)                  # +++
            self.pushButton_2.clicked.connect(self.clear)                     # !!!
            self.pushButton_3.clicked.connect(self.stop_start)
    
        def initMainWindow(self):                                        
            self.flag = True                                       
            self.pcenter = 0
            self.prange = 10
            self.step = 0.1
    
            s = Symbol('x')
            self.expr = self.textEdit_6.toPlainText()
            self.expr_1 = sympify(self.expr)
            self.func = lambdify(s, self.expr)
            
            period  = np.arange(self.pcenter-self.prange, self.pcenter+self.prange, self.step)
            lr = float(self.textEdit_8.toPlainText())
            self.myNN = PartyNN(learning_rate=lr, input_nodes=1, hidden_nodes=int(self.textEdit_10.toPlainText()), output_nodes=1)
            self.set_count =  int(self.textEdit_11.toPlainText())
            rand_set = np.random.random(self.set_count)*2*self.prange-self.prange+self.pcenter
    
        def start_func(self):                                                  # +++
            self.initMainWindow()                                              # +++
            self.draw()                                                        # +++
        
        def clear(self):
            self.flag = False                                                  # +++
    #        self.graphicsView.clear()                                         # ---
            QtCore.QTimer.singleShot(4000, self.graphicsView.clear)            # +++
            self.graphicsView_2.clear()
            
        def stop_start(self): 
            self.flag = not self.flag
            if self.flag:
               self.pushButton_3.setText("Остановить процесс аппроксимации")
               self.draw() 
            else:
                self.pushButton_3.setText("Возобновить процесс аппроксимации") 
            
        #Функция обучения
        def Train(self):
            rand_set = np.random.random(self.set_count)*2*self.prange-self.prange+self.pcenter
            for x in range(0, self.set_count-1):
                self.myNN.train([rand_set[x]], self.func(rand_set[x]))
    
        def MSE(y, Y):
            return np.mean((y-Y)**2)
    
        def draw(self):
            period  = np.arange(self.pcenter-self.prange, self.pcenter+self.prange, self.step)
            epochs = int(self.textEdit_7.toPlainText())
            self.graphicsView.addLegend()
            self.graphicsView_2.addLegend()  
            self.graphicsView.showGrid(x = True, y = True, alpha = 1.0)
            self.graphicsView_2.showGrid(x = True, y = True, alpha = 1.0)
            for e in range(epochs):
               for tr in range(50):
                   self.Train() 
                   app.processEvents()                                                
                   if not self.flag:                                          
                       break                                                                                                          
               self.graphicsView.clear()                                                      
               self.graphicsView.plot(period, self.func(period),pen=pg.mkPen('g',width=3),name = 'Исходная функция f(x):'+self.textEdit_6.toPlainText())          
               rand_set = np.random.random(self.set_count)*2*self.prange-self.prange+self.pcenter
               result_set=np.zeros(100)
               MSEe = []
               for x in range(0, self.set_count-1):
                   result_set[x] = self.myNN.predict([rand_set[x]])
                   MSEe.append(np.mean(self.func(rand_set[x]) - result_set[x])**2)
                   self.graphicsView.plot([rand_set[x]],[result_set[x]], pen=(1,2),symbol='o')               
                   app.processEvents()  
               self.graphicsView_2.clear() 
               self.graphicsView_2.plot(MSEe,pen=pg.mkPen('b',width=3),name='MSE(график среднеквадратич. ошибки)')
               if not self.flag:                                              
                   break                                                       
        
        def closeEvent(self, event):
            reply = QMessageBox.question\
            (self, 'Информация',
                "Вы уверены, что хотите уйти?",
                 QMessageBox.Yes,
                 QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.flag = False
                event.accept()
            else:
                event.ignore()      
            
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        w = MainWindow()
        w.show()
        sys.exit(app.exec_())

Ответы (0 шт):