Nano Hash - криптовалюты, майнинг, программирование

Как загрузить тяжелую модель при использовании шаблона проектирования MVC

Я создаю приложение с помощью wxPython и модели глубокого обучения, которую я создал с помощью Tensorflow. Я использую шаблон проектирования MVC. Моя проблема в том, что модель глубокого обучения очень тяжелая, и для ее загрузки требуется очень много времени (около 2 минут), а тем временем графический интерфейс зависает. Я создал пример кода, который описывает процесс. Вот как выглядит графический интерфейс при загрузке:

введите здесь описание изображения

и вот как выглядит графический интерфейс после загрузки: введите здесь описание изображения

Вопрос в том, как заставить приложение работать во время загрузки модели? Я также хотел бы добавить в графический интерфейс строку состояния, указывающую, что модель загружается или уже загружена.

Я добавляю пример кода, показывающий, как создается мое приложение.

import wx
import time

class Model:
    def __init__(self):
        '''This part is simulating the loading of tensorflow'''
        x = 0
        while x < 15:
            time.sleep(1)
            print(x)
            x += 1

class View(wx.Frame):
    def __init__(self, parent, title):
        super(View, self).__init__(parent, title=title, size=(400, 400))
        self.InitUI()

    def InitUI(self):
        # Defines the GUI controls
        masterPanel = wx.Panel(self)
        masterPanel.SetBackgroundColour("gold")
        self.vbox = wx.BoxSizer(wx.VERTICAL)
        self.fgs = wx.FlexGridSizer(6, 2, 10, 25)
        id = wx.StaticText(self, label="ID:")
        firstName = wx.StaticText(self, label="First name:")
        lastName = wx.StaticText(self, label="Last name:")
        self.idTc = wx.TextCtrl(self)
        self.firstNameTc = wx.TextCtrl(self)
        self.lastNameTc = wx.TextCtrl(self)
        self.fgs.AddMany([id, (self.idTc, 1, wx.EXPAND),
                         firstName, (self.firstNameTc, 1, wx.EXPAND),
                         lastName, (self.lastNameTc, 1, wx.EXPAND)])
        self.vbox.Add(self.fgs, proportion=1, flag=wx.ALL | wx.EXPAND,
   border=15)
        self.SetSizer(self.vbox)
        self.vbox.Fit(self)
        self.Layout()

class Controller:
    def __init__(self):
        self.view = View(None, title='Test')
        self.view.Show()
        self.model = Model()

def main():
    app = wx.App()
    controller = Controller()
    app.MainLoop()

if __name__ == '__main__':
    main()

Ответы:


1

Вы должны вызывать wx.GetApp().Yield() после команды self.view.Show(), которая на мгновение передает управление MainLoop.
Если код загрузки модели выполняется поэтапно, вы также можете периодически вызывать Yield во время загрузки.
Ниже приведен простой метод информирование пользователя о том, что что-то происходит. Если бы вы хотели отменить загрузку модели, вам пришлось бы обернуть ее в диалоговое окно, предполагая, что она загружается постепенно.

import wx
import time

class Model:
    def __init__(self):
        '''This part is simulating the loading of tensorflow'''
        x = 0
        #If the model load is perform in increments you could call wx.Yield
        # between the increments.
        while x < 15:
            time.sleep(1)
            wx.GetApp().Yield()
            print(x)
            x += 1

class View(wx.Frame):
    def __init__(self, parent, title):
        super(View, self).__init__(parent, title=title, size=(400, 400))
        self.InitUI()

    def InitUI(self):
        # Defines the GUI controls
        #masterPanel = wx.Panel(self)
        #masterPanel.SetBackgroundColour("gold")
        self.vbox = wx.BoxSizer(wx.VERTICAL)
        self.fgs = wx.FlexGridSizer(6, 2, 10, 25)
        id = wx.StaticText(self, label="ID:")
        firstName = wx.StaticText(self, label="First name:")
        lastName = wx.StaticText(self, label="Last name:")
        self.idTc = wx.TextCtrl(self)
        self.firstNameTc = wx.TextCtrl(self)
        self.lastNameTc = wx.TextCtrl(self)
        self.fgs.AddMany([id, (self.idTc, 1, wx.EXPAND),
                         firstName, (self.firstNameTc, 1, wx.EXPAND),
                         lastName, (self.lastNameTc, 1, wx.EXPAND)])
        self.vbox.Add(self.fgs, proportion=1, flag=wx.ALL | wx.EXPAND,
   border=15)
        self.CreateStatusBar() # A Statusbar in the bottom of the window
        self.StatusBar.SetStatusText("No model loaded", 0)
        self.SetSizer(self.vbox)
        self.vbox.Fit(self)
        self.Layout()

class Controller:
    def __init__(self):
        self.view = View(None, title='Test')
        self.view.Show()
        self.view.SetStatusText("Model loading", 0)
        wait = wx.BusyInfo("Please wait, loading model...")
        #Optionally add parent to centre message on self.view
        #wait = wx.BusyInfo("Please wait, loading model...", parent=self.view)
        wx.GetApp().Yield()
        self.model = Model()
        self.view.SetStatusText("Model loaded", 0)
        del wait

def main():
    app = wx.App()
    controller = Controller()
    app.MainLoop()

if __name__ == '__main__':
    main()
11.06.2019

2

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

Однако имейте в виду, что если у вас есть wx.Button в графическом интерфейсе, привязанный к method A, а method A требует загрузки полной модели для правильной работы, тогда пользователи смогут щелкнуть wx.Button, и программа, вероятно, зависнет, потому что модель все еще загружен не полностью. Если это так, вы можете использовать методы Disable() (во время загрузки модели) и Enable() (после загрузки модели), чтобы предотвратить это.

Код с комментариями (####).

import wx
import time
import _thread
from pubsub import pub

class Model:
    def __init__(self):
        '''This part is simulating the loading of tensorflow'''
        x = 0
        while x < 15:
            time.sleep(1)
            print(x)
            #### This is how you broadcast the 'Loading' message 
            #### from a different thread.
            wx.CallAfter(pub.sendMessage, 'Loading', x=x)
            x += 1
        #### The same as before
        wx.CallAfter(pub.sendMessage, 'Loading', x=x)

class View(wx.Frame):
    def __init__(self, parent, title):
        super(View, self).__init__(parent, title=title, size=(400, 400))
        self.InitUI()

    def InitUI(self):
        # Defines the GUI controls
        #### It needed to set the size of the panel to cover the frame
        #### because it was not covering the entire frame before
        masterPanel = wx.Panel(self, size=(400, 400))
        masterPanel.SetBackgroundColour("gold")
        self.vbox = wx.BoxSizer(wx.VERTICAL)
        self.fgs = wx.FlexGridSizer(6, 2, 10, 25)
        id = wx.StaticText(self, label="ID:")
        firstName = wx.StaticText(self, label="First name:")
        lastName = wx.StaticText(self, label="Last name:")
        self.idTc = wx.TextCtrl(self)
        self.firstNameTc = wx.TextCtrl(self)
        self.lastNameTc = wx.TextCtrl(self)
        self.fgs.AddMany([id, (self.idTc, 1, wx.EXPAND),
                         firstName, (self.firstNameTc, 1, wx.EXPAND),
                         lastName, (self.lastNameTc, 1, wx.EXPAND)])
        self.vbox.Add(self.fgs, proportion=1, flag=wx.ALL | wx.EXPAND,
   border=15)
        self.SetSizer(self.vbox)
        self.vbox.Fit(self)
        self.Layout()

        #### Create status bar to show loading progress. 
        self.statusbar = self.CreateStatusBar(1)
        self.statusbar.SetStatusText('Loading model')
        #### Set the size of the window because the status bar steals space
        #### in the height direction.
        self.SetSize(wx.DefaultCoord, 160)
        #### Subscribe the class to the message 'Loading'. This means that every
        #### time the meassage 'Loading' is broadcast the method 
        #### ShowLoadProgress will be executed.
        pub.subscribe(self.ShowLoadProgress, 'Loading')
        #### Start the thread that will load the model
        _thread.start_new_thread(self.LoadModel, ('test',))

    def LoadModel(self, test):
        """
        Load the Model
        """
        #### Change depending on how exactly are you going to create/load the 
        #### model
        self.model = Model()

    def ShowLoadProgress(self, x):
        """
        Show the loading progress 
        """
        if x < 15:
            self.statusbar.SetStatusText('Loading progress: ' + str(x))
        else:
            self.statusbar.SetStatusText('All loaded')

class Controller:
    def __init__(self):
        self.view = View(None, title='Test')
        self.view.Show()
        #### The line below is not needed now because the model is 
        #### loaded now from the thread started in View.InitUI
        #self.model = Model()

def main():
    app = wx.App()
    controller = Controller()
    app.MainLoop()

if __name__ == '__main__':
    main()

Если вы загружаете модель из метода внутри class View, вам не понадобится модуль PyPubSub, потому что вы можете просто вызвать wx.CallAfter(self.ShowLoadProgress, x)

11.06.2019
  • Я предпочитаю этот ответ моему первоначальному, упрощенному. 11.06.2019

  • 3

    Просто для удовольствия и потому, что я предпочитаю ответ, который дал kbr85 моему упрощенному первому ответу, вот вариант Threaded с gauge в statusbar и курсором Busy, хотя моя программа для создания снимков экрана его не распознала.
    Есть Stop Кнопка statusbar удаляется после завершения загрузки.
    Вместо использования pubsub я использовал для связи wxpython event.

    import wx
    import time
    from threading import Thread
    import wx.lib.newevent
    progress_event, EVT_PROGRESS_EVENT = wx.lib.newevent.NewEvent()
    load_status=["Model Loading","Model Loaded","Model Cancelled"]
    
    class Model(Thread):
        def __init__(self,parent):
            Thread.__init__(self)
            '''This thread simulates the loading of tensorflow'''
            self.stopthread = 0
            self.target = parent
            self.start()
    
        def run(self):
            while not self.stopthread:
                for i in range(20):
                    if self.stopthread:
                        break
                    time.sleep(0.5)
                    evt = progress_event(count=i, status=self.stopthread)
                    wx.PostEvent(self.target, evt)
                if self.stopthread == 0:
                    self.stopthread = 1
            evt = progress_event(count=i, status=self.stopthread)
            wx.PostEvent(self.target, evt)
    
        def terminate(self):
            self.stopthread = 2
    
    class View(wx.Frame):
        def __init__(self, parent, title):
            super(View, self).__init__(parent, title=title, size=(400, 400))
            self.InitUI()
    
        def InitUI(self):
            self.vbox = wx.BoxSizer(wx.VERTICAL)
            self.fgs = wx.FlexGridSizer(6, 2, 10, 25)
            id = wx.StaticText(self, label="ID:")
            firstName = wx.StaticText(self, label="First name:")
            lastName = wx.StaticText(self, label="Last name:")
            self.idTc = wx.TextCtrl(self)
            self.firstNameTc = wx.TextCtrl(self)
            self.lastNameTc = wx.TextCtrl(self)
            self.stop = wx.Button(self, -1, "Stop Load")
    
            self.fgs.AddMany([id, (self.idTc, 1, wx.EXPAND),
                             firstName, (self.firstNameTc, 1, wx.EXPAND),
                             lastName, (self.lastNameTc, 1, wx.EXPAND),
                             (self.stop,1,wx.EXPAND)])
    
            self.vbox.Add(self.fgs, proportion=1, flag=wx.ALL | wx.EXPAND,border=15)
            #Bind to the progress event issued by the thread
            self.Bind(EVT_PROGRESS_EVENT, self.OnProgress)
            #Bind to Stop button
            self.Bind(wx.EVT_BUTTON, self.OnStop)
            #Bind to Exit on frame close
            self.Bind(wx.EVT_CLOSE, self.OnExit)
            self.SetSizer(self.vbox)
            self.Layout()
    
            self.statusbar = self.CreateStatusBar(2)
            self.text = wx.StaticText(self.statusbar,-1,("No Model loaded"))
            self.progress = wx.Gauge(self.statusbar, range=20)
            sizer = wx.BoxSizer(wx.HORIZONTAL)
            sizer.Add(self.text, 0, wx.ALIGN_TOP|wx.ALL, 5)
            sizer.Add(self.progress, 1, wx.ALIGN_TOP|wx.ALL, 5)
            self.statusbar.SetSizer(sizer)
            wx.BeginBusyCursor()
            self.loadthread = Model(self)
    
    
        def OnProgress(self, event):
            self.text.SetLabel(load_status[event.status])
            #self.progress.SetValue(event.count)
            #or for indeterminate progress
            self.progress.Pulse()
            if event.status != 0:
                self.statusbar.Hide()
                wx.EndBusyCursor()
                self.Layout()
    
        def OnStop(self, event):
            if self.loadthread.isAlive():
                self.loadthread.terminate() # Shutdown the thread
                self.loadthread.join() # Wait for it to finish
    
        def OnExit(self, event):
            if self.loadthread.isAlive():
                self.loadthread.terminate() # Shutdown the thread
                self.loadthread.join() # Wait for it to finish
            self.Destroy()
    
    class Controller:
        def __init__(self):
            self.view = View(None, title='Test')
            self.view.Show()
    
    def main():
        app = wx.App()
        controller = Controller()
        app.MainLoop()
    
    if __name__ == '__main__':
        main()
    

    введите здесь описание изображения

    11.06.2019
  • Теперь мой ответ тот, который выглядит простым :). Я полностью согласен с тем, что лучше использовать wx.lib.newevent, чем pubsub. Я просто не знал, как правильно им пользоваться, теперь знаю, спасибо. Я просто добавил в код строчку sizer.Fit(self.statusbar), так как без нее виджеты отображаются некорректно при работе на macOS. 11.06.2019
  • Новые материалы

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

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..