نوشته‌ها

کار با Thread ها در زبان سی شارپ – آشنایی با فضای نام System.Threading و کلاس Thread

تا اینجا متوجه شدیم که چگونه می توان با کمک Delegate ها کدها را در یک Thread جداگانه و به صورت Asynchrnonous اجرا کرد. در ادامه مباحث مرتبط با برنامه نویسی Asynchronous به سراغ فضای نام System.Threading می رویم. این فضای نام شامل یکسری کلاس است که روند نوشتن برنامه Multi-Threaded را آسان می کند. کلاس های زیادی در این فضای نام وجود دارد که هر یک استفاده خاص خودش را دارد. در زیر با توضیح اولیه برخی از کلاس های این فضای نام آشنا می شویم:

  1. Interlocked: از این کلاس برای اجرای عملیات های atomic یا Atomic Operations بر روی متغیرهایی که در بین چندین Thread به اشتراک گذاشته شدند استفاده می شود.
  2. Monitor: از این کلاس برای پیاده سازی Synchronization بر روی اشیاء ای که Thread به آن دسترسی دارند استفاده می شود. در سی شارپ کلمه کلیدی lock در پشت زمینه از مکانیزم Monitor برای Synchronization استفاده می کنید که در بخش های بعدی با این تکنیک بیشتر آشنا می شویم.
  3. Mutex: از این کلاس برای اعمال Synchronization بین AppDomain ها استفاده می شود.
  4. ParameterizedThreadStart: این delegate به thread ها این اجازه را می دهد تا متدهایی پارامتر ورودی دارند را فراخوانی کند.
  5. Semaphor: از این کلاس برای محدود کردن تعداد Thread هایی که می توانند به یک Resource دسترسی داشته باشند استفاده می شود.
  6. Thread: به ازای هر Thread ایجاد شده برای برنامه باید یک کلاس از نوع Thread ایجاد کرد. در حقیقت کلاس Thread نقش اصلی را در ایجاد و استفاده از Thread ها دارد.
  7. ThreadPool: بوسیله این کلاس می توان به Thread-Pool مدیریت شده توسط خود CLR دسترسی داشت.
  8. ThreadPriority: بوسیله این enum می توان درجه اهمیت یک Thread را مشخص می کند.
  9. ThreadStart: از این delegate برای مشخص کردن متدی که در Thread باید اجرا شود استفاده می شود. این delegate بر خلاف ParameterizedThreadStart پارامتری قبول نمیکند.
  10. ThreadState: بوسیله این enum می توان وضعیت جاری یک thread را مشخص کرد.
  11. Timer: بوسیله کلاس Timer می توان مکانیزمی پیاده کرد که کدهای مورد نظر در بازه های زمانی خاص، مثلاً هر ۵ ثانیه یکبار و در یک Thread مجزا اجرا شوند.
  12. TimerCallBack: از این delegate برای مشخص کردن کدی که داخل timer باید اجرا شود استفاده می شود.

کلاس System.Threading.Thread

کلاس Thread اصلی ترین کلاس موجود در فضای نام System.Threading است. از یک کلاس برای دسترسی به Thread هایی که در روند اجرای یک AppDomain ایجاد شده اند استفاده می شود. همچنین بوسیله این کلاس می تواند Thread های جدید را نیز ایجاد کرد. کلاس Thread شامل یکسری متد و خصوصیت است که در این قسمت می خواهیم با آن ها آشنا شویم. ابتدا به سراغ خصوصیت CurrentThread که یک خصوصیت static در کلاس Thread است می رویم. بوسیله این خصوصیت می توان اطلاعات Thread جاری را بدست آورد. برای مثال در صورتی که در متد Main از این خصوصیت استفاده شود می توان به اطلاعات مربوط به Thread اصلی برنامه دسترسی داشت یا اگر برای یک متد Thread جداگانه ای ایجاد شود، در صورت استفاده از این خصوصیت در بدنه متد به اطلاعات Thread ایجاد شده دسترسی خواهیم داشت. در ابتدا با یک مثال می خواهیم اطلاعات Thread اصلی برنامه را بدست آوریم:

var primaryThread = Thread.CurrentThread;
primaryThread.Name = "PrimaryThread";
Console.WriteLine("Thread Name: {0}", primaryThread.Name);
Console.WriteLine("Thread AppDomain: {0}", Thread.GetDomain().FriendlyName);
Console.WriteLine("Thread Context Id: {0}", Thread.CurrentContext.ContextID);
Console.WriteLine("Thread Statred: {0}", primaryThread.IsAlive);
Console.WriteLine("Thread Priority: {0}", primaryThread.Priority);
Console.WriteLine("Thread State: {0}", primaryThread.ThreadState);

دقت کنید در ابتدا با به Thread یک نام دادیم. در صورتی که نامی برای Thread انتخاب نشود خصوصیت Name مقدار خالی بر میگرداند. مهمترین مزیت تخصیص نام برای Thread راحت تر کردن امکان debug کردن کد است. در Visual Studio پنجره ای وجود دارد به نام Threads که می توانید در زمان اجرای برنامه از طریق منوی Debug->Windows->Threads به آن دسترسی داشته باشید. در تصویر زیر نمونه ای از این پنجره را در زمان اجرا مشاهده می کنید:

آشنایی با فضای نام System.Threading و کلاس Thread

اما بریم سراغ موضوع اصلی، یعنی ایجاد Thread و اجرای آن. در ابتدا در مورد مراحل ایجاد یک Thread صحبت کنیم، معمولاً برای ایجاد یک Thread مراحل زیر را باید انجام دهیم:

  1. در ابتدا باید متدی ایجاد کنیم که وظیفه آن انجام کاری است که قرار است در یک Thread جداگانه انجام شود.
  2. در مرحله بعد باید یکی از delegate های ParameterizedThreadStart برای متدهایی که پارامتر ورودی دارند یا ThreadStart برای متدهای بدون پارامتر را انتخاب کرده و یک شئ از آن ایجاد کنیم که به عنوان سازنده متد مورد نظر به آن پاس داده می شود.
  3. از روی کلاس Thread یک شئ جدید ایجاد کرده و به عنوان سازنده شئ ای که از روی delegate های گفته شده در مرحله ۲ ساختیم را به آن ارسال کنیم.
  4. اطلاعات و تنظیمات اولیه مورد نظر برای Thread مانند Name یا Priority را برای آن ست کنیم.
  5. متد Start را در کلاس Thread را برای شروع کار Thread فراخوانی کنیم. با این کار متدی که در مرحله ۲ مشخص کردیم در یک Thread جداگانه اجرا می شود.

دقت کنید در مرحله ۲ می بایست بر اساس signature متدی که قصد اجرای آن در thread جداگانه را داریم، delegate مناسب انتخاب شود. همچنین ParameterizedThreadStart پارامتری که به عنوان ورودی قبول می کند از نوع Object است، یعنی اگر می خواهید چندین پارامتر به آن ارسال کنید می بایست حتماً یک کلاس یا struct ایجاد کرده و آن را به عنوان ورودی به کلاس Start ارسال کنید. با یک مثال ساده که از ThreadStart برای اجرای Thread استفاده می کند شروع می کنیم:

static void Main(string[] args)
{
    ThreadStart threadStart = new ThreadStart(PrintNumbers);
    Thread thread = new Thread(threadStart);
    thread.Name = "PrintNumbersThread";
    thread.Start();
    while (thread.IsAlive)
    {
        Console.WriteLine("Running in primary thread...");
        Thread.Sleep(2000);
    }
    Console.WriteLine("All done.");
    Console.ReadKey();
}

public static void PrintNumbers()
{
    for (int counter = 0; counter < 10; counter++)
    {
        Console.WriteLine("Running from thread: {0}", counter + 1);
        Thread.Sleep(500);
    }
}

در ابتدا متدی تعریف کردیم با نام PrintNumbers که قرار است در یک Thread مجزا اجرا شود. همانطور که مشاهده می کنید این متد نه پارامتر ورودی دارد و نه مقدار خروجی، پس از ThreadStart استفاده می کنیم. بعد از ایجاد شئ از روی ThreadStart و ایجاد Thread، نام Thread را مشخص کرده و متد Start را فراخوانی کردیم. به حلقه while ایجاد شده دقت کنید، در این حلقه بوسیله خصوصیت IsAlive گفتیم تا زمانی که Thread ایجاد شده در حال اجرا است کد داخل while اجرا شود. همچنین بوسیله متد Sleep در متد Main و متد PrintNumbers در عملیات اجرا برای Thread های مربوط به متد تاخیر ایجاد کردیم. بعد اجرای کد بالا خروجی زیر نمایش داده می شود:

Running in primary thread...
Running from thread: 1
Running from thread: 2
Running from thread: 3
Running from thread: 4
Running in primary thread...
Running from thread: 5
Running from thread: 6
Running from thread: 7
Running from thread: 8
Running in primary thread...
Running from thread: 9
Running from thread: 10
All done.

در قدم بعدی فرض کنید که قصد داریم بازه اعدادی که قرار است در خروجی چاپ شود را به عنوان پارامتر ورودی مشخص کنیم، در اینجا ابتدا یک کلاس به صورت زیر تعریف می کنیم:

public class PrintNumberParameters
{
    public int Start { get; set; }
    public int Finish { get; set; }
}

در قدم بعدی کلاس PrintNumbers را به صورت زیر تغییر می دهیم:

public static void PrintNumbers(object data)
{
    PrintNumberParameters parameters = (PrintNumberParameters) data;
    for (int counter = parameters.Start; counter < parameters.Finish; counter++)
    {
        Console.WriteLine("Running from thread: {0}", counter);
        Thread.Sleep(500);
    }
}

همانطور که مشاهده می کنید، پارامتر ورودی PrintNumbers از نوع object است و در بدنه ورودی را به کلاس PrintNumberParameters تبدیل کرده و از آن استفاده کردیم. در مرحله بعد متد Main را باید تغییر داده و به جای ThreadStart از ParameterizedThreadStart استفاده کنیم، همچنین به عنوان پارامتر ورودی برای متد Start شئ ای از PrintNumberParameters ایجاد کرده و با عنوان پارامتر به آن ارسال می کنیم:

ParameterizedThreadStart threadStart = new ParameterizedThreadStart(PrintNumbers);
Thread thread = new Thread(threadStart);
thread.Name = "PrintNumbersThread";
thread.Start(new PrintNumberParameters() {Start = 5, Finish = 13});
while (thread.IsAlive)
{
    Console.WriteLine("Running in primary thread...");
    Thread.Sleep(2000);
}
Console.WriteLine("All done.");
Console.ReadKey();

با اعمال تغییرات ذکر شده و اجرای کد، اعداد بر اساس بازه مشخص شده در خروجی چاپ می شوند. در این قسمت از مطلب مربوط به Thread ها با نحوه ایجاد و استفاده از Thread ها آشنا شدیم. در قسمت های بعدی به مباحث دیگری در مورد Thread ها خواهیم پرداخت.

منبع


قسمت اول آموزش-برنامه نویسی Asynchronous – آشنایی با Process ها، Thread ها و AppDomain ها

قسمت دوم آموزش- آشنایی با ماهیت Asynchronous در Delegate ها

قسمت سوم آموزش-آشنایی با فضای نام System.Threading و کلاس Thread

قسمت چهارم آموزش- آشنایی با Thread های Foreground و Background در دات نت

قسمت پنجم آموزش- آشنایی با مشکل Concurrency در برنامه های Multi-Threaded و راهکار های رفع این مشکل

قسمت ششم آموزش- آشنایی با کلاس Timer در زبان سی شارپ

قسمت هفتم آموزش-آشنایی با CLR ThreadPool در دات نت

قسمت هشتم آموزش- مقدمه ای بر Task Parallel Library و کلاس Parallel در دات نت

قسمت نهم آموزش- برنامه نویسی Parallel:آشنایی با کلاس Task در سی شارپ

قسمت دهم آموزش-برنامه نویسی Parallel در سی شارپ :: متوقف کردن Task ها در سی شارپ – کلاس CancellationToken

قسمت یازدهم آموزش- برنامه نویسی Parallel در سی شارپ :: کوئری های Parallel در LINQ

قسمت دوازدهم آموزش- آشنایی با کلمات کلیدی async و await در زبان سی شارپ

قسمت سیزدهم آموزش- استفاده از متد WhenAll برای اجرای چندین Task به صورت همزمان در سی شارپ

مقدمه

حذف نویز تصاویر _ گروهی از محققان سیستمی را توسعه داده اند که با استفاده از هوش مصنوعی و بدون نیاز به عکس های واضح از منبع، نویز تصاویر را از بین می برد.

شرح خبر

این گروه متشکل از محققان انویدیا، MIT و دانشگاه آلتو در توسعه این سیستم از یادگیری عمیق بهره برده اند که بر خلاف روش های قبلی نیازی به مشاهده نمونه های کامل از تصویر مورد نظر داشته و تنها با استفاده از داده های ناقص یا دو تصویر غیر واضح به افزایش کیفیت تصاویر می پردازد. علاوه بر این نتیجه نهایی افزایش کیفیت، حذف متون یا اصلاح تصویر نسبت به روش های قبلی به مراتب بهتر است.

یادگیری عمیق گونه ای از یادگیری ماشینی است که در آن سیستم با کمک هوش مصنوعی نحوه بازیابی تصاویر دارای نویز از طریق کنار هم قرار دادن تصاویر، متون یا ویدیوها را فرا می گیرد. یکی دیگر از قابلیت های جالب توجه سیستم جدید افزایش کیفیت تصاویر در عرض چند میلی ثانیه است.
مبنای کار هوش مصنوعی در این سیستم بر شبکه عصبی استوار است که با استفاده از تصاویر دارای نویز آموزش دیده است. در این روش هوش مصنوعی علی رغم عدم نیاز به تصاویر واضح از منبع باید دوبار تصویر را مشاهده کند.

آزمایشات این گروه نشان داده که از تصاویر تخریب شده از طریق نویزهایی نظیر «گاوسی افزایشی»، «پواسون» یا ترکیب آنها می توان برای تولید تصاویر بهینه ای استفاده کرد که کیفیت آن‌ها با تصاویر بازیابی‌ شده از عکس های بدون مشکل تقریبا برابر است.
کاربردهای علمی این سیستم مبتنی بر یادگیری عمیق شامل زمینه های پزشکی است که در آن می توان کیفیت اسکن های MRI و تصاویر دیگر را به شکل چشمگیری افزایش داد.

چند ماه قبل نیز تیم تحقیقاتی انستیتوی «ماکس پلانک» به رهبری دکتر مهدی سجادی، الگوریتمی را توسعه داده بودند که با بهره گیری از هوش مصنوعی وضوح تصاویر بی کیفیت را تا حد زیادی بهبود می بخشید.

 

مرحله ۴: سرکوب لبه های غیر حداکثر

آخرین مرحله، پیدا کردن لبه های ضعیف که موازی با لبه های قوی هستند و از بین بردن آنهاست. این عمل، با بررسی پیکسل های عمود بر یک پیکسل لبه خاص و حذف لبه های غیر حداکثرانجام شده است. کد مورد استفاده بسیار مشابه کد ردیابی لبه است.

 

</pre>
<pre>#include "stdafx.h"
#include "tripod.h"
#include "tripodDlg.h"

#include "LVServerDefs.h"
#include "math.h"
#include <fstream>
#include <string>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

using namespace std;

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTripodDlg dialog

CTripodDlg::CTripodDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CTripodDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CTripodDlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	//////////////// Set destination BMP to NULL first 
	m_destinationBitmapInfoHeader = NULL;

}

////////////////////// Additional generic functions

static unsigned PixelBytes(int w, int bpp)
{
    return (w * bpp + 7) / 8;
}

static unsigned DibRowSize(int w, int bpp)
{
    return (w * bpp + 31) / 32 * 4;
}

static unsigned DibRowSize(LPBITMAPINFOHEADER pbi)
{
    return DibRowSize(pbi->biWidth, pbi->biBitCount);
}

static unsigned DibRowPadding(int w, int bpp)
{
    return DibRowSize(w, bpp) - PixelBytes(w, bpp);
}

static unsigned DibRowPadding(LPBITMAPINFOHEADER pbi)
{
    return DibRowPadding(pbi->biWidth, pbi->biBitCount);
}

static unsigned DibImageSize(int w, int h, int bpp)
{
    return h * DibRowSize(w, bpp);
}

static size_t DibSize(int w, int h, int bpp)
{
    return sizeof (BITMAPINFOHEADER) + DibImageSize(w, h, bpp);
}

/////////////////////// end of generic functions


void CTripodDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CTripodDlg)
	DDX_Control(pDX, IDC_PROCESSEDVIEW, m_cVideoProcessedView);
	DDX_Control(pDX, IDC_UNPROCESSEDVIEW, m_cVideoUnprocessedView);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CTripodDlg, CDialog)
	//{{AFX_MSG_MAP(CTripodDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDEXIT, OnExit)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTripodDlg message handlers

BOOL CTripodDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here

	// For Unprocessed view videoportal (top one)
	char sRegUnprocessedView[] = "HKEY_LOCAL_MACHINE\\Software\\UnprocessedView";
	m_cVideoUnprocessedView.PrepareControl("UnprocessedView", sRegUnprocessedView, 0 );	
	m_cVideoUnprocessedView.EnableUIElements(UIELEMENT_STATUSBAR,0,TRUE);
	m_cVideoUnprocessedView.ConnectCamera2();
	m_cVideoUnprocessedView.SetEnablePreview(TRUE);

	// For binary view videoportal (bottom one)
	char sRegProcessedView[] = "HKEY_LOCAL_MACHINE\\Software\\ProcessedView";
	m_cVideoProcessedView.PrepareControl("ProcessedView", sRegProcessedView, 0 );	
	m_cVideoProcessedView.EnableUIElements(UIELEMENT_STATUSBAR,0,TRUE);
	m_cVideoProcessedView.ConnectCamera2();
	m_cVideoProcessedView.SetEnablePreview(TRUE);

	// Initialize the size of binary videoportal
	m_cVideoProcessedView.SetPreviewMaxHeight(240);
	m_cVideoProcessedView.SetPreviewMaxWidth(320);

	// Uncomment if you wish to fix the live videoportal's size
	// m_cVideoUnprocessedView.SetPreviewMaxHeight(240);
	// m_cVideoUnprocessedView.SetPreviewMaxWidth(320);

	// Find the screen coodinates of the binary videoportal
	m_cVideoProcessedView.GetWindowRect(m_rectForProcessedView);
	ScreenToClient(m_rectForProcessedView);
	allocateDib(CSize(320, 240));

	// Start grabbing frame data for Procssed videoportal (bottom one)
	m_cVideoProcessedView.StartVideoHook(0);

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CTripodDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CTripodDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CTripodDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CTripodDlg::OnExit() 
{
	// TODO: Add your control notification handler code here

	// Kill live view videoportal (top one)
	m_cVideoUnprocessedView.StopVideoHook(0);
    m_cVideoUnprocessedView.DisconnectCamera();	
	
	// Kill binary view videoportal (bottom one)
	m_cVideoProcessedView.StopVideoHook(0);
    m_cVideoProcessedView.DisconnectCamera();	

	// Kill program
	DestroyWindow();	

	

}

BEGIN_EVENTSINK_MAP(CTripodDlg, CDialog)
    //{{AFX_EVENTSINK_MAP(CTripodDlg)
	ON_EVENT(CTripodDlg, IDC_PROCESSEDVIEW, 1 /* PortalNotification */, OnPortalNotificationProcessedview, VTS_I4 VTS_I4 VTS_I4 VTS_I4)
	//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()

void CTripodDlg::OnPortalNotificationProcessedview(long lMsg, long lParam1, long lParam2, long lParam3) 
{
	// TODO: Add your control notification handler code here
	
	// This function is called at the camera's frame rate
    
#define NOTIFICATIONMSG_VIDEOHOOK	۱۰

	// Declare some useful variables
	// QCSDKMFC.pdf (Quickcam MFC documentation) p. 103 explains the variables lParam1, lParam2, lParam3 too 
	
	LPBITMAPINFOHEADER lpBitmapInfoHeader; // Frame's info header contains info like width and height
	LPBYTE lpBitmapPixelData; // This pointer-to-long will point to the start of the frame's pixel data
    unsigned long lTimeStamp; // Time when frame was grabbed

	switch(lMsg) {
		case NOTIFICATIONMSG_VIDEOHOOK:
			{
				lpBitmapInfoHeader = (LPBITMAPINFOHEADER) lParam1; 
				lpBitmapPixelData = (LPBYTE) lParam2;
				lTimeStamp = (unsigned long) lParam3;

				grayScaleTheFrameData(lpBitmapInfoHeader, lpBitmapPixelData);
				doMyImageProcessing(lpBitmapInfoHeader); // Place where you'd add your image processing code
				displayMyResults(lpBitmapInfoHeader);

			}
			break;

		default:
			break;
	}	
}

void CTripodDlg::allocateDib(CSize sz)
{
	// Purpose: allocate information for a device independent bitmap (DIB)
	// Called from OnInitVideo

	if(m_destinationBitmapInfoHeader) {
		free(m_destinationBitmapInfoHeader);
		m_destinationBitmapInfoHeader = NULL;
	}

	if(sz.cx | sz.cy) {
		m_destinationBitmapInfoHeader = (LPBITMAPINFOHEADER)malloc(DibSize(sz.cx, sz.cy, 24));
		ASSERT(m_destinationBitmapInfoHeader);
		m_destinationBitmapInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
		m_destinationBitmapInfoHeader->biWidth = sz.cx;
		m_destinationBitmapInfoHeader->biHeight = sz.cy;
		m_destinationBitmapInfoHeader->biPlanes = 1;
		m_destinationBitmapInfoHeader->biBitCount = 24;
		m_destinationBitmapInfoHeader->biCompression = 0;
		m_destinationBitmapInfoHeader->biSizeImage = DibImageSize(sz.cx, sz.cy, 24);
		m_destinationBitmapInfoHeader->biXPelsPerMeter = 0;
		m_destinationBitmapInfoHeader->biYPelsPerMeter = 0;
		m_destinationBitmapInfoHeader->biClrImportant = 0;
		m_destinationBitmapInfoHeader->biClrUsed = 0;
	}
}

void CTripodDlg::displayMyResults(LPBITMAPINFOHEADER lpThisBitmapInfoHeader)
{
	// displayMyResults: Displays results of doMyImageProcessing() in the videoport
	// Notes: StretchDIBits stretches a device-independent bitmap to the appropriate size

	CDC				*pDC;	// Device context to display bitmap data
	
	pDC = GetDC();	
	int nOldMode = SetStretchBltMode(pDC->GetSafeHdc(),COLORONCOLOR);

	StretchDIBits( 
		pDC->GetSafeHdc(),
		m_rectForProcessedView.left,				// videoportal left-most coordinate
		m_rectForProcessedView.top,					// videoportal top-most coordinate
		m_rectForProcessedView.Width(),				// videoportal width
		m_rectForProcessedView.Height(),			// videoportal height
		۰,											// Row position to display bitmap in videoportal
		۰,											// Col position to display bitmap in videoportal
		lpThisBitmapInfoHeader->biWidth,			// m_destinationBmp's number of columns
		lpThisBitmapInfoHeader->biHeight,			// m_destinationBmp's number of rows
		m_destinationBmp,							// The bitmap to display; use the one resulting from doMyImageProcessing
		(BITMAPINFO*)m_destinationBitmapInfoHeader, // The bitmap's header info e.g. width, height, number of bits etc
		DIB_RGB_COLORS,								// Use default 24-bit color table
		SRCCOPY										// Just display
	);
 
	SetStretchBltMode(pDC->GetSafeHdc(),nOldMode);

	ReleaseDC(pDC);

	// Note: 04/24/02 - Added the following:
	// Christopher Wagner cwagner@fas.harvard.edu noticed that memory wasn't being freed

	// Recall OnPortalNotificationProcessedview, which gets called everytime
	// a frame of data arrives, performs 3 steps:
	// (۱) grayScaleTheFrameData - which mallocs m_destinationBmp
	// (۲) doMyImageProcesing
	// (۳) displayMyResults - which we're in now
	// Since we're finished with the memory we malloc'ed for m_destinationBmp
	// we should free it: 
	
	free(m_destinationBmp);

	// End of adds
}

void CTripodDlg::grayScaleTheFrameData(LPBITMAPINFOHEADER lpThisBitmapInfoHeader, LPBYTE lpThisBitmapPixelData)
{

	// grayScaleTheFrameData: Called by CTripodDlg::OnPortalNotificationBinaryview
	// Task: Read current frame pixel data and computes a grayscale version

	unsigned int	W, H;			  // Width and Height of current frame [pixels]
	BYTE            *sourceBmp;		  // Pointer to current frame of data
	unsigned int    row, col;
	unsigned long   i;
	BYTE			grayValue;

	BYTE			redValue;
	BYTE			greenValue;
	BYTE			blueValue;

    W = lpThisBitmapInfoHeader->biWidth;  // biWidth: number of columns
    H = lpThisBitmapInfoHeader->biHeight; // biHeight: number of rows

	// Store pixel data in row-column vector format
	// Recall that each pixel requires 3 bytes (red, blue and green bytes)
	// m_destinationBmp is a protected member and declared in binarizeDlg.h

	m_destinationBmp = (BYTE*)malloc(H*3*W*sizeof(BYTE));

	// Point to the current frame's pixel data
	sourceBmp = lpThisBitmapPixelData;

	for (row = 0; row < H; row++) {
		for (col = 0; col < W; col++) {

			// Recall each pixel is composed of 3 bytes
			i = (unsigned long)(row*3*W + 3*col);
        
			// The source pixel has a blue, green andred value:
			blueValue  = *(sourceBmp + i);
			greenValue = *(sourceBmp + i + 1);
			redValue   = *(sourceBmp + i + 2);

			// A standard equation for computing a grayscale value based on RGB values
			grayValue = (BYTE)(0.299*redValue + 0.587*greenValue + 0.114*blueValue);

			// The destination BMP will be a grayscale version of the source BMP
			*(m_destinationBmp + i)     = grayValue;
			*(m_destinationBmp + i + 1) = grayValue;
			*(m_destinationBmp + i + 2) = grayValue;
			
		}
	}
}


void CTripodDlg::doMyImageProcessing(LPBITMAPINFOHEADER lpThisBitmapInfoHeader)
{
	// doMyImageProcessing:  This is where you'd write your own image processing code
	// Task: Read a pixel's grayscale value and process accordingly

	unsigned int	W, H;			// Width and Height of current frame [pixels]
	unsigned int    row, col;		// Pixel's row and col positions
	unsigned long   i;				// Dummy variable for row-column vector
	int	    upperThreshold = 60;	// Gradient strength nessicary to start edge
	int		lowerThreshold = 30;	// Minimum gradient strength to continue edge
	unsigned long iOffset;			// Variable to offset row-column vector during sobel mask
	int rowOffset;					// Row offset from the current pixel
	int colOffset;					// Col offset from the current pixel
	int rowTotal = 0;				// Row position of offset pixel
	int colTotal = 0;				// Col position of offset pixel
	int Gx;							// Sum of Sobel mask products values in the x direction
	int Gy;							// Sum of Sobel mask products values in the y direction
	float thisAngle;				// Gradient direction based on Gx and Gy
	int newAngle;					// Approximation of the gradient direction
	bool edgeEnd;					// Stores whether or not the edge is at the edge of the possible image
	int GxMask[3][3];				// Sobel mask in the x direction
	int GyMask[3][3];				// Sobel mask in the y direction
	int newPixel;					// Sum pixel values for gaussian
	int gaussianMask[5][5];			// Gaussian mask

	W = lpThisBitmapInfoHeader->biWidth;  // biWidth: number of columns
    H = lpThisBitmapInfoHeader->biHeight; // biHeight: number of rows
	
	for (row = 0; row < H; row++) {
		for (col = 0; col < W; col++) {
			edgeDir[row][col] = 0;
		}
	}

	/* Declare Sobel masks */
	GxMask[0][0] = -1; GxMask[0][1] = 0; GxMask[0][2] = 1;
	GxMask[1][0] = -2; GxMask[1][1] = 0; GxMask[1][2] = 2;
	GxMask[2][0] = -1; GxMask[2][1] = 0; GxMask[2][2] = 1;
	
	GyMask[0][0] =  1; GyMask[0][1] =  2; GyMask[0][2] =  1;
	GyMask[1][0] =  0; GyMask[1][1] =  0; GyMask[1][2] =  0;
	GyMask[2][0] = -1; GyMask[2][1] = -2; GyMask[2][2] = -1;

	/* Declare Gaussian mask */
	gaussianMask[0][0] = 2;		gaussianMask[0][1] = 4;		gaussianMask[0][2] = 5;		gaussianMask[0][3] = 4;		gaussianMask[0][4] = 2;	
	gaussianMask[1][0] = 4;		gaussianMask[1][1] = 9;		gaussianMask[1][2] = 12;	gaussianMask[1][3] = 9;		gaussianMask[1][4] = 4;	
	gaussianMask[2][0] = 5;		gaussianMask[2][1] = 12;	gaussianMask[2][2] = 15;	gaussianMask[2][3] = 12;	gaussianMask[2][4] = 2;	
	gaussianMask[3][0] = 4;		gaussianMask[3][1] = 9;		gaussianMask[3][2] = 12;	gaussianMask[3][3] = 9;		gaussianMask[3][4] = 4;	
	gaussianMask[4][0] = 2;		gaussianMask[4][1] = 4;		gaussianMask[4][2] = 5;		gaussianMask[4][3] = 4;		gaussianMask[4][4] = 2;	
	

	/* Gaussian Blur */
	for (row = 2; row < H-2; row++) {
		for (col = 2; col < W-2; col++) {
			newPixel = 0;
			for (rowOffset=-2; rowOffset<=2; rowOffset++) {
				for (colOffset=-2; colOffset<=2; colOffset++) {
					rowTotal = row + rowOffset;
					colTotal = col + colOffset;
					iOffset = (unsigned long)(rowTotal*3*W + colTotal*3);
					newPixel += (*(m_destinationBmp + iOffset)) * gaussianMask[2 + rowOffset][2 + colOffset];
				}
			}
			i = (unsigned long)(row*3*W + col*3);
			*(m_destinationBmp + i) = newPixel / 159;
		}
	}

	/* Determine edge directions and gradient strengths */
	for (row = 1; row < H-1; row++) {
		for (col = 1; col < W-1; col++) {
			i = (unsigned long)(row*3*W + 3*col);
			Gx = 0;
			Gy = 0;
			/* Calculate the sum of the Sobel mask times the nine surrounding pixels in the x and y direction */
			for (rowOffset=-1; rowOffset<=1; rowOffset++) {
				for (colOffset=-1; colOffset<=1; colOffset++) {
					rowTotal = row + rowOffset;
					colTotal = col + colOffset;
					iOffset = (unsigned long)(rowTotal*3*W + colTotal*3);
					Gx = Gx + (*(m_destinationBmp + iOffset) * GxMask[rowOffset + 1][colOffset + 1]);
					Gy = Gy + (*(m_destinationBmp + iOffset) * GyMask[rowOffset + 1][colOffset + 1]);
				}
			}

			gradient[row][col] = sqrt(pow(Gx,2.0) + pow(Gy,2.0));	// Calculate gradient strength			
			thisAngle = (atan2(Gx,Gy)/3.14159) * 180.0;		// Calculate actual direction of edge
			
			/* Convert actual edge direction to approximate value */
			if ( ( (thisAngle < 22.5) && (thisAngle > -22.5) ) || (thisAngle > 157.5) || (thisAngle < -157.5) )
				newAngle = 0;
			if ( ( (thisAngle > 22.5) && (thisAngle < 67.5) ) || ( (thisAngle < -112.5) && (thisAngle > -157.5) ) )
				newAngle = 45;
			if ( ( (thisAngle > 67.5) && (thisAngle < 112.5) ) || ( (thisAngle < -67.5) && (thisAngle > -112.5) ) )
				newAngle = 90;
			if ( ( (thisAngle > 112.5) && (thisAngle < 157.5) ) || ( (thisAngle < -22.5) && (thisAngle > -67.5) ) )
				newAngle = 135;
				
			edgeDir[row][col] = newAngle;		// Store the approximate edge direction of each pixel in one array
		}
	}

	/* Trace along all the edges in the image */
	for (row = 1; row < H - 1; row++) {
		for (col = 1; col < W - 1; col++) {
			edgeEnd = false;
			if (gradient[row][col] > upperThreshold) {		// Check to see if current pixel has a high enough gradient strength to be part of an edge
				/* Switch based on current pixel's edge direction */
				switch (edgeDir[row][col]){		
					case 0:
						findEdge(0, 1, row, col, 0, lowerThreshold);
						break;
					case 45:
						findEdge(1, 1, row, col, 45, lowerThreshold);
						break;
					case 90:
						findEdge(1, 0, row, col, 90, lowerThreshold);
						break;
					case 135:
						findEdge(1, -1, row, col, 135, lowerThreshold);
						break;
					default :
						i = (unsigned long)(row*3*W + 3*col);
						*(m_destinationBmp + i) = 
						*(m_destinationBmp + i + 1) = 
						*(m_destinationBmp + i + 2) = 0;
						break;
					}
				}
			else {
				i = (unsigned long)(row*3*W + 3*col);
					*(m_destinationBmp + i) = 
					*(m_destinationBmp + i + 1) = 
					*(m_destinationBmp + i + 2) = 0;
			}	
		}
	}
	
	/* Suppress any pixels not changed by the edge tracing */
	for (row = 0; row < H; row++) {
		for (col = 0; col < W; col++) {	
			// Recall each pixel is composed of 3 bytes
			i = (unsigned long)(row*3*W + 3*col);
			// If a pixel's grayValue is not black or white make it black
			if( ((*(m_destinationBmp + i) != 255) && (*(m_destinationBmp + i) != 0)) || ((*(m_destinationBmp + i + 1) != 255) && (*(m_destinationBmp + i + 1) != 0)) || ((*(m_destinationBmp + i + 2) != 255) && (*(m_destinationBmp + i + 2) != 0)) ) 
				*(m_destinationBmp + i) = 
				*(m_destinationBmp + i + 1) = 
				*(m_destinationBmp + i + 2) = 0; // Make pixel black
		}
	}

	/* Non-maximum Suppression */
	for (row = 1; row < H - 1; row++) {
		for (col = 1; col < W - 1; col++) {
			i = (unsigned long)(row*3*W + 3*col);
			if (*(m_destinationBmp + i) == 255) {		// Check to see if current pixel is an edge
				/* Switch based on current pixel's edge direction */
				switch (edgeDir[row][col]) {		
					case 0:
						suppressNonMax( 1, 0, row, col, 0, lowerThreshold);
						break;
					case 45:
						suppressNonMax( 1, -1, row, col, 45, lowerThreshold);
						break;
					case 90:
						suppressNonMax( 0, 1, row, col, 90, lowerThreshold);
						break;
					case 135:
						suppressNonMax( 1, 1, row, col, 135, lowerThreshold);
						break;
					default :
						break;
				}
			}	
		}
	}
	
}

void CTripodDlg::findEdge(int rowShift, int colShift, int row, int col, int dir, int lowerThreshold)
{
	int W = 320;
	int H = 240;
	int newRow;
	int newCol;
	unsigned long i;
	bool edgeEnd = false;

	/* Find the row and column values for the next possible pixel on the edge */
	if (colShift < 0) {
		if (col > 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col < W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;		// If the next pixel would be off image, don't do the while loop
	if (rowShift < 0) {
		if (row > 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row < H - 1) {
		newRow = row + rowShift;
	} else
		edgeEnd = true;	
		
	/* Determine edge directions and gradient strengths */
	while ( (edgeDir[newRow][newCol]==dir) && !edgeEnd && (gradient[newRow][newCol] > lowerThreshold) ) {
		/* Set the new pixel as white to show it is an edge */
		i = (unsigned long)(newRow*3*W + 3*newCol);
		*(m_destinationBmp + i) =
		*(m_destinationBmp + i + 1) =
		*(m_destinationBmp + i + 2) = 255;
		if (colShift < 0) {
			if (newCol > 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol < W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift < 0) {
			if (newRow > 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow < H - 1) {
			newRow = newRow + rowShift;
		} else
			edgeEnd = true;	
	}	
}

void CTripodDlg::suppressNonMax(int rowShift, int colShift, int row, int col, int dir, int lowerThreshold)
{
	int W = 320;
	int H = 240;
	int newRow = 0;
	int newCol = 0;
	unsigned long i;
	bool edgeEnd = false;
	float nonMax[320][3];			// Temporarily stores gradients and positions of pixels in parallel edges
	int pixelCount = 0;					// Stores the number of pixels in parallel edges
	int count;						// A for loop counter
	int max[3];						// Maximum point in a wide edge
	
	if (colShift < 0) {
		if (col > 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col < W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;		// If the next pixel would be off image, don't do the while loop
	if (rowShift < 0) {
		if (row > 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row < H - 1) {
		newRow = row + rowShift;
	} else
		edgeEnd = true;	
	i = (unsigned long)(newRow*3*W + 3*newCol);
	/* Find non-maximum parallel edges tracing up */
	while ((edgeDir[newRow][newCol] == dir) && !edgeEnd && (*(m_destinationBmp + i) == 255)) {
		if (colShift < 0) {
			if (newCol > 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol < W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift < 0) {
			if (newRow > 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow < H - 1) {
			newRow = newRow + rowShift;
		} else
			edgeEnd = true;	
		nonMax[pixelCount][0] = newRow;
		nonMax[pixelCount][1] = newCol;
		nonMax[pixelCount][2] = gradient[newRow][newCol];
		pixelCount++;
		i = (unsigned long)(newRow*3*W + 3*newCol);
	}

	/* Find non-maximum parallel edges tracing down */
	edgeEnd = false;
	colShift *= -1;
	rowShift *= -1;
	if (colShift < 0) {
		if (col > 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col < W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;	
	if (rowShift < 0) {
		if (row > 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row < H - 1) {
		newRow = row + rowShift;
	} else
		edgeEnd = true;	
	i = (unsigned long)(newRow*3*W + 3*newCol);
	while ((edgeDir[newRow][newCol] == dir) && !edgeEnd && (*(m_destinationBmp + i) == 255)) {
		if (colShift < 0) {
			if (newCol > 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol < W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift < 0) {
			if (newRow > 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow < H - 1) {
			newRow = newRow + rowShift;
		} else
			edgeEnd = true;	
		nonMax[pixelCount][0] = newRow;
		nonMax[pixelCount][1] = newCol;
		nonMax[pixelCount][2] = gradient[newRow][newCol];
		pixelCount++;
		i = (unsigned long)(newRow*3*W + 3*newCol);
	}

	/* Suppress non-maximum edges */
	max[0] = 0;
	max[1] = 0;
	max[2] = 0;
	for (count = 0; count < pixelCount; count++) {
		if (nonMax[count][2] > max[2]) {
			max[0] = nonMax[count][0];
			max[1] = nonMax[count][1];
			max[2] = nonMax[count][2];
		}
	}
	for (count = 0; count < pixelCount; count++) {
		i = (unsigned long)(nonMax[count][0]*3*W + 3*nonMax[count][1]);
		*(m_destinationBmp + i) = 
		*(m_destinationBmp + i + 1) = 
		*(m_destinationBmp + i + 2) = 0;
	}
}

 

دانلود کد فوق از طریق لینک زیر:

Canny in C++ -No2

رمز فایل : behsanandish.com

الگوریتم Canny در سی پلاس پلاس قسمت ۱
الگوریتم Canny در سی پلاس پلاس قسمت ۲
الگوریتم Canny در سی پلاس پلاس قسمت ۳
الگوریتم Canny در سی پلاس پلاس قسمت ۴

آشنایی با کلمات کلیدی async و await در زبان سی شارپ

تا این لحظه از مجموعه مطالب مرتبط با مباحث Asynchronous Programming در سی شارپ با ماهیت Asynchronous در delegate ها، کار با Thread ها و کتابخانه TPL در دات نت آشنا شدیم. اما باز هم در برخی سناریو ها و انجام کارهای پیچیده در برنامه نویسی Asynchronous، نیاز به حجم زیادی از کدها دارد.

از نسخه ۴٫۵ دات، در زبان سی شارپ (و همینطور زبان VB) دو کلمه کلیدی اضافه شد که اجازه نوشتن کدهای Asynchronous را به شکل دیگری به برنامه نویسان می داد. این دو کلمه کلیدی، کلمات async و await هستند و زمانی که شما در کدهای خود از این دو کلمه کلیدی استفاده می کنید، در زمان کامپایل کدها، کامپایلر کدهایی را برای شما تولید می کند که به صورت بهینه و البته مطمئن کارهای Asynchronous را برای شما انجام می دهند، کدهای تولید شده از کلاس هایی که در فضای نام System.Threading.Tasks قرار دارند استفاده می کنند.

نگاه اولیه با ساختار async و await

زمانی که شما در بخشی از کد خود از کلمه کلیدی async و بر روی متدها، عبارات لامبدا یا متدهای بدون نام استفاده می کنید، در حقیقت می گویید که این قطعه کد به صورت خودکار باید به صورت Asynchronous فراخوانی شود و زمان استفاده از کدی که به صورت async تعریف شده، CLR به صورت خودکار thread جدیدی ایجاد کرده و کد را اجرا می کند. اما زمان فراخوانی کدهایی که به صورت async تعریف شده اند، استفاده از کلمه await این امکان را فراهم می کند که اجرای thread جاری تا زمان تکمیل اجرای کدی که به صورت async تعریف شده، می بایست متوقف شود.

برای آشنایی بیشتر برنامه ای از نوع Windows Forms Application ایجاد کرده، یک Button بر روی فرم قرار می دهیم. زمانی که بر روی Button ایجاد شده کلیک می شود، یک متد دیگر فراخوانی شده و بعد از یک وقفه ۱۰ ثانیه ای عبارتی را بر میگرداند و در نهایت این متن به عنوان Title برای فرم برنامه ست می شود:

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
    }
 
    private void CallButton_Click(object sender, EventArgs e)
    {
        this.Text = DoWork();
    }
 
    private string DoWork()
    {
        Thread.Sleep(10000);
        return "Done.";
    }
}

مشکلی که وجود دارد این است که بعد از کلیک بر روی Button ایجاد شده، ۱۰ ثانیه باید منتظر شده تا عنوان فرم تغییر کند. اما با انجام یکسری تغییرات در کد بالا، می توان بوسیله کلمات کلیدی async و await کاری کرد که عملیات اجرای متد به صورت Asynchronous انجام شود. برای اینکار کد بالا را به صورت زیر تغییر می دهیم:

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
    }
 
    private async void CallButton_Click(object sender, EventArgs e)
    {
        this.Text = await DoWork();
    }
 
    private Task<string> DoWork()
    {
        return Task.Run(() = >
        {
            Thread.Sleep(10000);
            return "Done.";
        });
    }
}

بعد از اجرای برنامه، خواهیم دید که فرم ما به قول معروف block نمی شود، یعنی تا زمان اتمام فراخوانی DoWork می توانیم کارهای دیگری در فرم انجام دهیم. اگر در کد بالا دقت کنید، متدی که برای رویداد Click دکمه CallButton تعریف شده، با کلمه کلیدی async مشخص شده، یعنی اجرای این متد باید به صورت Aynchronous انجام شود.

علاوه بر این، داخل بدنه این متد، زمان فراخوانی DoWork از کلمه await استفاده کردیم، دقت کنید که نوشتن کلمه کلیدی await اینجا الزامی است، اگر این کلمه کلیدی نوشته نشود، زمان اجرای DoWork باز هم عملیات فراخوانی متد باعث block شدن فرم ما می شود. همچنین دقت کنید که متد DoWork به جای اینکه مقدار string برگرداند، مقداری از نوع <Task<string بر میگرداند. به طور خلاصه کاری که DoWork انجام می دهد به صورت زیر است:

زمانی که متد DoWork فراخوانی می شود، یک Task جدید اجرا می شود و داخل Task ابتدا عملیات اجرای Thread به مدت ۱۰ ثانیه متوقف می شود و بعد از ۱۰ ثانیه یک رشته به عنوان خروجی برگردانده می شود. البته این رشته تحت یک شئ از نوع Task به متدی که DoWork را فراخوانی کرده بازگردانده می شود.

با تعریف بالا، شاید بتوان بهتر نقش کلمه کلیدی await را متوجه شد، زمانی که برنامه به کلمه کلیدی await می رسد، در حقیقت منتظر می ماند تا عملیات فراخوانی متدی که await قبل از آن نوشته شده به اتمام برسد، سپس مقدار خروجی از داخل Task مربوطه برداشته شده و داخل خصوصیت Text قرار داده می شود.

قواعد نام گذاری برای متدهای Async

همانطور که گفتیم، داخل متدهایی که با async مشخص شده اند، حتماً می بایست کلمه کلیدی await نیز نوشته شود. اما از کجا بدانیم کدام متدها می توانند به صورت Async فراخوانی شوند؟ یعنی نوع خروجی آن ها یک Task است؟ اصطلاحاً به متدهایی که خروجی آن ها از نوع <Task<T است Awaitable گفته می شود. برای اینکار باید از قواعد نامگذاری متدهای Async پیروی کنیم. بر اساس مستندات مایکروسافت، می بایست کلیه متدهایی که مقدار خروجی آن ها از نوع Task است، به صورت async تعریف شوند و در انتهای نام متد کلمه Async نوشته شود، بر اساس مطالب گفته شده، متد DoWork را به صورت زیر تغییر می دهیم:

private async Task<string> DoWorkAsync()
{
    return await Task.Run(() = >
    {
        Thread.Sleep(10000);
        return "Done.";
    });
}

با انجام تغییرات بالا، کد رویداد Click را برای CallButton به صورت زیر تغییر می دهیم:

private async void CallButton_Click(object sender, EventArgs e)
{
    this.Text = await DoWorkAsync();
}

متدهای Async با مقدار خروجی void

در صورتی که متدی که قرار است به صورت async فراخوانی شود، مقدار خروجی ندارد می توان نوع خروجی متد را از نوع کلاس غیر جنریک Task انتخاب کرد و کلمه کلیدی return را ننوشت:

private async Task DoWorkAsync()
{
    await Task.Run(() = >
    {
        Thread.Sleep(10000);
    });
}

فراخوانی این متد نیز به صورت زیر خواهد بود:

await DoWorkAsync();
MessageBox.Show("Done.");

متدهای async با چندین await

یکی از قابلیت های async و await، نوشتن چندین قسمت await در یک متد async است. نمونه کد زیر حالت گفته شده را نشان می دهد:

private async void CallButton_Click(object sender, EventArgs e)
{
    await Task.Run(() = > { Thread.Sleep(5000); });
    MessageBox.Show("First Task Done!");
 
    await Task.Run(() = > { Thread.Sleep(5000); });
    MessageBox.Show("Second Task Done!");
 
    await Task.Run(() = > { Thread.Sleep(5000); });
    MessageBox.Show("Third Task Done!");
}

دقت کنید که برای await های بالا متدی تعریف نکردیم و تنها در مقابل آن متد Run از کلاس Task را فراخوانی کردیم. البته این موضوع ربطی به چند await بودن متد ندارد و شما می تواند متد هایی که خروجی آن ها از نوع Task است را نیز فراخوانی کنید، این حالت تنها برای مثال به این صورت نوشته شده است.

منبع


قسمت اول آموزش-برنامه نویسی Asynchronous – آشنایی با Process ها، Thread ها و AppDomain ها

قسمت دوم آموزش- آشنایی با ماهیت Asynchronous در Delegate ها

قسمت سوم آموزش-آشنایی با فضای نام System.Threading و کلاس Thread

قسمت چهارم آموزش- آشنایی با Thread های Foreground و Background در دات نت

قسمت پنجم آموزش- آشنایی با مشکل Concurrency در برنامه های Multi-Threaded و راهکار های رفع این مشکل

قسمت ششم آموزش- آشنایی با کلاس Timer در زبان سی شارپ

قسمت هفتم آموزش-آشنایی با CLR ThreadPool در دات نت

قسمت هشتم آموزش- مقدمه ای بر Task Parallel Library و کلاس Parallel در دات نت

قسمت نهم آموزش- برنامه نویسی Parallel:آشنایی با کلاس Task در سی شارپ

قسمت دهم آموزش-برنامه نویسی Parallel در سی شارپ :: متوقف کردن Task ها در سی شارپ – کلاس CancellationToken

قسمت یازدهم آموزش- برنامه نویسی Parallel در سی شارپ :: کوئری های Parallel در LINQ

قسمت دوازدهم آموزش- آشنایی با کلمات کلیدی async و await در زبان سی شارپ

قسمت سیزدهم آموزش- استفاده از متد WhenAll برای اجرای چندین Task به صورت همزمان در سی شارپ

 

برنامه نویسی Parallel در سی شارپ :: مقدمه ای بر Task Parallel Library و کلاس Parallel در دات نت

پیش از این ما در سری مطالب مرتبط با بحث کار با Thread با نحوه ایجاد و مدیریت Thread ها در دات نت آشنا شدیم. از نسخه ۴ دات نت قابلیتی اضافه شد با نام Task Parallel Programming یا TPL که روش جدیدی برای نوشتن برنامه Multi-Theaded است. این قابلیت بوسیله یکسری از کلاس ها که در فضای نام System.Threading.Tasks قرار دارد فراهم شده و به ما این اجازه را می دهد که بدون درگیر شدن مستقیم با Thread ها و Thread Pool ها برنامه های Multi-Threaded بنوسیم.

دقت کنید که زمان استفاده از قابلیت TPL دیگر نیازی به استفاده از کلاس های فضای نام System.Threading نمی باشد و به صورت پشت زمینه عملیات ساخت و مدیریت Thread ها برای ما انجام می شود. با این کار شیوه کار با Threadها بسیار ساده شده و یکسری از پیچیدگی ها در این بین حذف می شود.

فضای نام System.Threading.Tasks

همانطور که گفتیم TPL در حقیقت مجموعه ای از کلاس ها است که در فضای نام System.Threading.Tasks قرار گرفته. یکی از قابلیت های TPL این است که کارهای محوله را به صورت خودکار بین CPU های سیستم (در صورت وجود) توزیع می کند که این کار در پشت زمینه بوسیله CLR Thread Pool انجام می شود.

کارهای انجام شده توسط TPL در پشت زمینه عبارتند از تقسیم بندی وظایف، زمانبندی Thread ها، مدیریت وضعیت (State Management) و یکسری از کارهای اصطلاحاً Low-Level دیگر. نتیجه این کار برای شما بالا رفتن کارآیی برنامه ها بوده بدون اینکه درگیر پیچیدگی های کار با Thread ها شوید. همانطور که گفتیم فضای نام System.Threading.Tasks شامل یکسری کلاس ها مانند کلاس Parallel، کلاس Task و … می باشد که در ادامه با این کلاس ها بیشتر آشنا می شویم.

نقش کلاس Parallel

یکی از کلاس های TPL که نقش کلیدی را در نوشتن کدهای Parallel ایفا می کند، کلاس Parallel است، این کلاس یکسری متدها در اختیار ما قرار می دهد که بتوانیم بر روی آیتم های یک مجموعه (علی الخصوص مجموعه هایی که اینترفیس IEnumerable را پیاده سازی کرده اند) به صورت parallel عملیات هایی را انجام دهیم.

متدهای این کلاس عبارتند از متد های For و ForEach که البته Overload های متفاوتی برای این متدها وجود دارد. بوسیله این متدها می توان کدهایی نوشتن که عملیات مورد نظر را به صورت parallel بر روی آیتم های یک مجموعه انجام دهند. دقت کنید کدهایی که برای این متدها نوشته می شوند در حقیقت همان کدهایی هستند که معمولاً در حلقه های for و foreach استفاده می شوند، با این تفاوت که به صورت parallel اجرا شده و اجرا و مدیریت کدها بوسیله thread ها و CLR Thread Pool انجام شده و البته بحث همزمانی نیز به صورت خودکار مدیریت می شود.

کار با متد ForEach

در ابتدا به سراغ متد ForEach می رویم، این متد یک مجموعه که ایترفیس IEnumerable را پیاده سازی کرده به عنوان پارامتر اول و متدی که باید بر روی هر یک اعضای این مجموعه انجام شود را به عنوان پارامتر دوم قبول می کند:

var numbers = new List &lt; int &gt; {2, 6, 8, 1, 3, 9, 6, 10, 5, 4};
Parallel.For(3, 6, index  = &gt;
{
    Console.WriteLine(numbers[index]);
    Console.WriteLine("Thread Id: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
});

در کد بالا یک آرایه از لیست از نوع int تعریف کرده و در مرحله بعد بوسیله متد ForEach در کلاس Parallel اعضای لیست را پردازش می کنیم، با هر بار اجرا خروجی های متفاوتی دریافت خواهیم کرد:

۸
۵
Thread Id: 6
۴
۲
Thread Id: 1
۶
۳
Thread Id: 5
۹
Thread Id: 5
۱۰
Thread Id: 5
۱
Thread Id: 5
Thread Id: 4
Thread Id: 6
۶
Thread Id: 1
Thread Id: 3

همانطور که مشاهده می کنید شناسه های مربوط به thread در هر بار اجرای کدی مشخص شده در متد ForEach با یکدگیر متفاوت است، دلیل این موضوع ایجاد و مدیریت Thread ها توسط CLR Thread Pool است که ممکن است با هر بار فراخوانی متد مشخص شده به عنوان پارامتر دوم یک thread جدید ایجاد شده یا عملیات در یک thread موجود انجام شود.

کار با متد For

اما علاوه بر متد ForEach متد For نیست را می توان برای پردازش یک مجموعه استفاده کرد. در ساده ترین حالت این متد یک عدد به عنوان اندیس شروع حلقه، عدد دوم به عنوان اندیس پایان حلقه و یک پارامتر که متدی با پارامتر ورودی از نوع int یا long که نشان دهنده اندیس جاری است قبول می کند، برای مثال در متد زیر بوسیله متد For لیست numbers را در خروجی چاپ می کنیم، اما نه همه خانه های آن را پس عبارت اند از:

var numbers = new List &lt; int &gt;  {2, 6, 8, 1, 3, 9, 6, 10, 5, 4};
Parallel.For(3, 6, index  = &gt;
{
    Console.WriteLine(numbers[index]);
    Console.WriteLine("Thread Id: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
});

با اجرای کد بالا خروجی زیر نمایش داده می شود، البته با هر بار اجرا ممکن است خروجی ها با هم متفاوت باشند:

۱
Thread Id: 1
۹
۳
Thread Id: 3
Thread Id: 4

یکی از کاربردی ترین موارد برای استفاده از کلاس Parallel و متدهای For و ForEach زمانی است که قصد داریم مجموعه حجیمی از اطلاعات را پردازش کنیم و البته پردازش هر المان وابسته به سایر المان ها نیست، زیرا عملیات پردازش المان ها به دلیل اینکه در Thread های مختلف انجام می شوند، ترتیبی در زمان اجرای المان ها در نظر گرفته نشده و ممکن است آیتمی در وسط لیست قبل از آیتم ابتدای لیست پردازش شود.

برای مثال، فرض کنید قصد دارید لیستی از تصاویر را گرفته و بر روی آن ها پردازشی انجام دهید یا لیستی از فایل ها را می خواهیم پردازش کنید، در اینجور مواقع به راحتی می توان از کلاس Parallel و متدهای آن استفاده کرد. یکی از مزیت های استفاده از کلاس Task این است که علاوه بر توزیع انجام کارها در میان Thread ها، در صورت موجود بودن بیش از یک CPU در سیستم شما، از سایر CPU ها هم برای پردازش اطلاعات استفاده می کند. در قسمت بعدی در مورد کلاس Task صحبت خواهیم کرد.

منبع



قسمت اول آموزش-برنامه نویسی Asynchronous – آشنایی با Process ها، Thread ها و AppDomain ها

قسمت دوم آموزش- آشنایی با ماهیت Asynchronous در Delegate ها

قسمت سوم آموزش-آشنایی با فضای نام System.Threading و کلاس Thread

قسمت چهارم آموزش- آشنایی با Thread های Foreground و Background در دات نت

قسمت پنجم آموزش- آشنایی با مشکل Concurrency در برنامه های Multi-Threaded و راهکار های رفع این مشکل

قسمت ششم آموزش- آشنایی با کلاس Timer در زبان سی شارپ

قسمت هفتم آموزش-آشنایی با CLR ThreadPool در دات نت

قسمت هشتم آموزش- مقدمه ای بر Task Parallel Library و کلاس Parallel در دات نت

قسمت نهم آموزش- برنامه نویسی Parallel:آشنایی با کلاس Task در سی شارپ

قسمت دهم آموزش-برنامه نویسی Parallel در سی شارپ :: متوقف کردن Task ها در سی شارپ – کلاس CancellationToken

قسمت یازدهم آموزش- برنامه نویسی Parallel در سی شارپ :: کوئری های Parallel در LINQ

قسمت دوازدهم آموزش- آشنایی با کلمات کلیدی async و await در زبان سی شارپ

قسمت سیزدهم آموزش- استفاده از متد WhenAll برای اجرای چندین Task به صورت همزمان در سی شارپ

لینکدین چیست؟

در عصر ارتباطی حال حاضر، استفاده مطلوب از شبکه های اجتماعی بسیار رشد داشته و تعداد کثیری از مردم جهان روی این شبکه ها ( که اکثر این شبکه وابستگی دولتی ندارند) فعال شده اند و از این شبکه ها استفاده می کنند. شبکه های اجتماعی مجازی، بهترین ابزاری است که می تواند شما را در دستیابی به اهداف حرفه ای یاری دهد. بسیاری از افراد هستند که در دنیای کسب و کار امروز در شبکه های اجتماعی مجازی به دنبال فرد متخصص یا خدمات خود هستند.

لینکدین در واقع یک شبکه اجتماعی حرفه ای است که اساسا برای بهبود کسب و کارهای مختلف و آشنا کردن دیگران با آن ها طراحی شده است. با این حال، استفاده از لینکدین تنها به این موضوع محدود نمی شود و کاربران این شبکه اجتماعی می توانند از طریق آن، با افراد دیگری که در حوزه کاری شان فعالیت می کنند، آشنا شوند و با آن ها ارتباط برقرار کنند.

شرکت بهسان اندیش به منظور ارائه خدمات و فعالیت های خود در شبکه های اجتماعی اقدام به راه اندازی صفحه ای شخصی نموده که علاقمندان می توانند از طریق لینک زیر در سایت لینکدین ما را دنبال نمایند:

ورود به صفحه شخصی شرکت بهسان اندیش در سایت لینکدین

 

صفحه شخیص شرکت بهسان اندیش در لینکدین

 

الگوریتم ژنتیک

  • نحوه نمایش مسئله:

می‌دانیم اگر دو وزیر در یک ستون قرار گیرند قطعاً به جواب نخواهیم رسید. بنابراین قرار دادن دو وزیر در یک ستون باعث غیرامیدبخش شدن جواب مسئله می‌شود.

برای نمایش مسئله در کروموزوم‌ها از این ویژگی استفاده کرده و به صورت زیر عمل می‌کنیم:

یک آرایه تک بعدی ایجاد می‌کنیم که به تعداد ستون‌های صفحه شطرنج عنصر دارد. هر عنصر از این آرایه نشان می‌دهد که وزیر در کدام سطر از آن ستون قرار دارد. به عنوان مثال اگر مسئله ۸ وزیر را در نظر بگیریم، آرایه تک بعدی باید دارای ۸ عنصر باشد. فرض کنید آرایه دارای مقادیر زیر باشد:

۸ , ۷ , ۶ , ۵ , ۴ , ۳ , ۲ , ۱

مقدار ۸ در اولین عنصر آرایه گویای این مطلب است که در ستون اول صفحه شطرنج وزیری در سطر هشتم قرار داده‌ایم.

  • تولید جمعیت اولیه:

الگوریتم‌های ژنتیک ابتدا جمعیت اولیه‌ای تولید کرده و سپس سعی در بهبود بخشیدن این جمعیت دارند. برای مسئله n وزیر تولید جمعیت به صورت تصادفی خواهد بود. بدین صورت که وزیرها به‌طور تصادفی روی صفحه شطرنج قرار می‌دهیم.

برای محاسبه میزان بهینگی جواب تعداد جفت وزیرهایی را که به هم گارد می‌دهند، محاسبه می‌کنیم. برای مسئله ۸ وزیر در بدترین حالت هر وزیر با همه وزیرهای دیگر گارد می‌دهد (فرض کنید همه وزیرها در یک سطر قرار گیرند). در این حالت حداکثر تعداد جفت وزیرهایی که به همگدیکر کارد می‌دهند ۲۸ جفت است:

۷ + ۶ + ۵ + ۴ +۳ + ۲ + ۱

در حالت کلی برای مسئله n وزیر حداکثر تعداد جفت وزیرهایی که به همدیگر گارد می‌دهند به صورت زیر محاسبه می‌شود:

۱+ ۲ +.. +(n-۱) = (n * (n-۱)) /۲
  • برای محاسبه میزان بهینگی هر کروموزوم از فرمول زیر استفاده می‌کنیم:
Fitness[i] =1 – (Guard(chromosome[i])) / MaxGuards
  • حداکثر تعداد گاردها:
MaxGuards
  • تعداد جفت وزیرهایی که در کروموزوم ام همدیگر را گارد می‌دهند:
 Guard(chromosome[i])

 

منبع

 


پیاده سازی الگوریتم ۸ وزیر با استفاده از الگوریتم ژنتیک

راهکاری که برای حل یک مسئله با الگوریتم ژنتیک استفاده می شود تکامل می یابد. الگوریتم ژنتیک مثل هر الگوریتم بهینه سازی دیگر با تعریف متغیرهای بهینه سازی آغاز می شود و مانند الگوریتم های بهنیه سازی دیگر نیز خاتمه می یابد یعنی با تست همگرایی.

یک الگوریتم GA دارای پارامترهای زیر است:

  • : Fitnessتابعی برای ارزیابی یک فرضیه  که مقداری عددی به هر فرضیه نسبت میدهد
  • : Fitness_threshold مقدار آستانه که شرط پایان را معین میکند
  • : population تعداد فرضیه هائی که باید در جمعیت در نظر گرفته شوند
  • : crossover rate  در صدی از جمعیت که در هر مرحله توسط الگوریتم crossover  جایگزین میشوند
  • :mutation rate  نرخ mutation

الگوریتم GA  به صورت زیر کار می کند:

  • : Initializeجمعیت را با تعداد population فرضیه بطور تصادفی مقدار دهی اولیه کنید.
  • : Evaluateبرای هر فرضیه h در population مقدار تابع Fitness(h) را محاسبه نمائید.
  • تا زمانیکه[maxh Fitness(h)] < Fitness_threshold یک جمعیت جدید ایجاد  کنید.
  • فرضیه ای که دارای بیشترین مقدار Fitness است را برگردانید.

روش های مختلف crossover:

Single-point crossover

  • یک نقطه تصادفی در طول رشته انتخاب میشود.
  • والدین در این نقطه به دوقسمت میشوند.
  • هر فرزند با انتخاب تکه اول از یکی از والدین و تکه دوم از والد دیگر بوجود میاید.

روشهای دیگر Crossover

در crossover یکنواخت بیتها بصورت یکنواخت از والدین انتخاب می شوند.

اپراتورهای ژنتیکی Mutation :

  • اپراتور mutation برای بوجود آوردن فرزند فقط از یک والد استفاده میکند. اینکار با انجام تغییرات کوچکی در رشته اولیه  بوقوع میپیوندد.
  • با استفاده از یک توزیع یکنواخت یک بیت بصورت تصادفی اتنخاب و مقدار آن تغییر پیدا میکند.
  • معمولا mutation بعد از انجام crossover اعمال میشود.

تابع fitness  معیاری برای رتبه بندی فرضیه هاست که کمک میکند تا فرضیه های برتر برای نسل بعدی جمعیت انتخاب شوند. نحوه انتخاب این تابع بسته به کاربر مورد نظر دارد

در روش معرفی شده در الگوریتم ساده GA احتمال انتخاب یک فرضیه برای استفاده در جمعیت بعدی بستگی به نسبت fitness  آن به fitness  بقیه اعضا دارد. این روش Roulette Wheel selectionنامیده میشود.

روش جستجوی GA با روشهای دیگر مثل شبکه های عصبی تفاوت دارد:

در شبکه عصبی روش Gradient descent بصورت  هموار از فرضیه ای به فرضیه  مشابه دیگری حرکت میکند در حالیکه GA  ممکن است بصورت ناگهانی فرضیه والد را با فرزندی جایگزین نماید که تفاوت اساسی با والد آن داشته باشد.از اینرو احتمال گیر افتادن GA در مینیمم محلی کاهش می یابد. با این وجود GA با مشکل دیگری روبروست که crowding  نامیده میشود crowding پدیده ای  است که در آن  عضوی که سازگاری بسیاربیشتری از بقیه افراد جمعیت دارد بطور مرتب تولید نسل کرده و با تولید اعضای مشابه درصد عمده ای از جمعیت را اشغال میکند. راه حل رفع مشکل Crowdingاستفاده از ranking  برای انتخاب نمونه ها است، با اختصاص رتبه به فرضیه ای که بسیار بهتر از بقیه عمل میکند.

مسئله ۸ وزیر:

بدین ترتیب دیدیم که مسیر میان اجزای الگوریتم ژنتیک به ترتیب زیر است:

  1. تعریف توابع و متغیرها
  2. تولید جمعیت اولیه
  3. دیکد کردن کروموزوم ها
  4. پیدا کردن هزینه برای هر کروموزوم
  5. انتخاب جفت ها
  6. جفت گیری
  7. میوتیشن
  8. بررسی همگرایی
  9. خاتمه یا بازگشت به مرحله دیکد کردن کروموزوم ها

ژن عددی از ۰ تا n-1 است در ۸ وزیر n برابر با ۸ است بنابراین ژن عددی از ۰ تا ۷ می شود و کروموزوم آرایه ای از ژن هاست. که می تواند پاسخ مسئله باشد.

جمعیت هر نسل می تواند  تعداد کروموزوم ها را تعیین کند.

جمعیت اولیه از انتخاب رندومی از کروموزوم ها ایجاد می شود. تعداد نسل هایی که برای همگرایی مورد نیاز است به جمعیت تصادفی اولیه بستگی دارد.

برای پیدا کردن هزینه مربوط به هر کروموزوم یک تابع هزینه تعریف می شود. نتیجه تابع هزینه یک cost value است که در نهایت میانگین cost valueهای هر نسل به نتیجه مطلوب نزدیک می شود.

کروموزوم هایی که فیتنس بالاتری (هزینه پایین تر) دارند برای تولید نسل بعدی استفاده می شوند.

در فرایند cross over فرزندان توسط والدین تولید می شوند که ترکیب آنها شامل ترکیب ژن های آنهاست. اگر نسل جدید حاوی کروموزومی باشد که نزدیک یا برابر با نتایج مطلوب باشد آنگاه مسئله حل شده است. در غیر اینصورت فرایند قبلی در نسل جدید هم پیاده سازی می شود مانند فرایندی که برای والدین آنها اتفاق افتاد. تا زمانی که به راه حل مناسب برسیم این روال ادامه دارد.

در شطرنج وزیر می تواند هر طور که مایل بود حرکت کند افقی عمودی یا در قطر. صفحه شطرنج ۸ در ۸ است یعنی ۸ سطر و ۸ ستون دارد . در مسئله ۸ وریز استاندارد به دنبال این هستیم که چگونه ۸ وزیر در خانه های جدول به گونه ای قرار بگیرند که هیچ یک دیگری را تهدید نکنند. در اینجا با الگوریتم ژنتیک این کار را انجام می دهیم.

برای تولید فرزندان از والیدن نیاز به crossover داریم که تصمیم می گیرد از دو والدین کدام ژن باید انتخاب شود.

 

مسئله چند وزیر قسمت ۱
مسئله چند وزیر قسمت ۲
مسئله چند وزیر قسمت ۳
مسئله چند وزیر قسمت ۴

۱٫A GABOR FILTER TEXTURE ANALYSIS APPROACH FOR HISTOPATHOLOGICAL BRAIN TUMOUR SUBTYPE DISCRIMINATION

Abstract Meningioma brain tumour discrimination is challenging as many histological patterns are mixed between the different subtypes. In clinical practice, dominant patterns are investigated for signs of specific meningioma pathology; however the simple observation could result in inter- and intra-observer variation due to the complexity of the histopathological patterns. Also employing a computerised feature extraction approach applied at a single resolution scale might not suffice in accurately delineating the mixture of histopathological patterns. In this work we propose a novel multiresolution feature extraction approach for characterising the textural properties of the different pathological patterns (i.e. mainly cell nuclei shape, orientation and spatial arrangement within the cytoplasm). The patterns’ textural properties are characterised at various scales and orientations for an improved separability between the different extracted features. The Gabor filter energy output of each magnitude response was combined with four other fixed-resolution texture signatures (2 model-based and 2 statistical-based) with and without cell nuclei segmentation. The highest classification accuracy of 95% was reported when combining the Gabor filters’ energy and the meningioma subimage fractal signature as a feature vector without performing any prior cell nuceli segmentation. This indicates that characterising the cell-nuclei self-similarity properties via Gabor filters can assists in achieving an improved meningioma subtype classification, which can assist in overcoming variations in reported diagnosis.
Keywords – texture analysis, Gabor filter, fractal dimension, meningioma histopathology, brain tumours

فایل PDF – در ۱۴ صفحه- نویسنده : Omar Sultan Al-Kadi

A GABOR FILTER TEXTURE ANALYSIS APPROACH FOR HISTOPATHOLOGICAL BRAIN TUMOUR SUBTYPE DISCRIMINATION

پسورد فایل : behsanandish.com


۲٫A Review Paper on Gabor Filter Algorithm & Its Applications

Abstract— In applications of image analysis and computer vision, Gabor filters have maintained their popularity in feature extraction. The reason behind this is that the resemblance between Gabor filter and receptive field of simple cells in visual cortex. Being successful in applications like face detection, iris recognition, fingerprint matching; where, Gabor feature based processes are amongst the best performers. The Gabor features can be derived by applying signal processing techniques both in time and frequency domain. The models like human preattentive texture perception have been proposed which involves steps like convolution, inhibition and texture boundary detection. Texture features are based on the local power spectrum obtained by a bank of Gabor filters. The concept of sparseness to generate novel contextual multiresolution texture descriptors are described. In this paper we present the detailed study about the Gabor filter and its application.
Index Terms— Gabor filter, Gabor energy, image quality assessment, Gabor features, multiresolution techniques, segmentation, textured images..

فایل PDF – در ۵ صفحه- نویسنده : Neelu Arora , Mrs. G. Sarvani

A Review Paper on Gabor Filter Algorithm & Its Applications

پسورد فایل : behsanandish.com


۳٫Comparison of texture features based on Gabor filters

 

Abstract -The performance of a number of texture feature operators is evaluated. The features are all based on the local spectrum which is obtained by a bank of Gabor filters. The comparison is made using a quantitative method which is based on Fisher’s criterion. It is shown that, in general, the discrimination effectiveness of the features increases with the amount of post-Gabor processing.

فایل PDF – در ۶ صفحه- نویسنده : P. Kruizinga, N. Petkov and S.E. Grigorescu

Comparison of texture features based on Gabor filters

پسورد فایل : behsanandish.com


۴٫Evolutionary Gabor Filter Optimization with Application to Vehicle Detection

 

Abstract—Despite the considerable amount of research work on the application of Gabor filters in pattern classification, their design and selection have been mostly done on a trial and error basis. Existing techniques are either only suitable for a small number of filters or less problem-oriented. A systematic and general evolutionary Gabor filter optimization (EGFO) approach that yields a more optimal, problem-specific, set of filters is proposed in this study. The EGFO approach unifies filter design with filter selection by integrating Genetic Algorithms (GAs) with an incremental clustering approach. Specifically, filter design is performed using GAs, a global optimization approach that encodes the parameters of the Gabor filters in a chromosome and uses genetic operators to optimize them. Filter selection is performed by grouping together filters having similar characteristics (i.e., similar parameters) using incremental clustering in the parameter space. Each group of filters is represented by a single filter whose parameters correspond to the average parameters of the filters in the group. This step eliminates redundant filters, leading to a compact, optimized set of filters. The average filters are evaluated using an application-oriented fitness criterion based on Support Vector Machines (SVMs). To demonstrate the effectiveness of the proposed framework, we have considered the challenging problem of vehicle detection from gray-scale images. Our experimental results illustrate that the set of Gabor filters, specifically optimized for the problem of vehicle detection, yield better performance than using traditional filter banks.

 

فایل PDF – در ۸ صفحه- نویسنده : Zehang Sun, George Bebis and Ronald Miller

Evolutionary Gabor Filter Optimization with Application to Vehicle Detection

پسورد فایل : behsanandish.com


۵٫Expression-Invariant Face Recognition via 3D Face Reconstruction Using Gabor Filter Bank from a 2D Single Image

Abstract— In this paper, a novel method for expression- insensitive face recognition is proposed from only a 2D single image in a gallery including any facial expressions. A 3D Generic Elastic Model (3D GEM) is used to reconstruct a 3D model of each human face in the present database using only a single 2D frontal image with/without facial expressions. Then, the rigid parts of the face are extracted from both the texture and reconstructed depth based on 2D facial land-marks. Afterwards, the Gabor filter bank was applied to the extracted rigid-part of the face to extract the feature vectors from both texture and reconstructed depth images. Finally, by combining 2D and 3D feature vectors, the final feature vectors are generated and classified by the Support Vector Machine (SVM). Favorable outcomes were acquired to handle expression changes on the available image database based on the proposed method compared to several state-of-the-arts in expression-insensitive face recognition.

Keywords—Face recognition; 3D shape recovery; Gesture and Behavior Analysis.

 

فایل PDF – در ۶ صفحه- نویسنده : Ali Moeini, Hossein Moeini, Karim Faez

Expression-Invariant Face Recognition via 3D Face Reconstruction Using Gabor Filter Bank from a 2D Single Image

پسورد فایل : behsanandish.com


۶٫IMAGE RETRIEVAL BASED ON HIERARCHICAL GABOR FILTERS

Content Based Image Retrieval (CBIR) is now a widely investigated issue that aims at allowing users of multimedia information systems to automatically retrieve images coherent with a sample image. A way to achieve this goal is the computation of image features such as the color, texture, shape, and position of objects within images, and the use of those features as query terms. We propose to use Gabor filtration properties in order to find such appropriate features. The article presents multichannel Gabor filtering and a hierarchical image representation. Then a salient (characteristic) point detection algorithm is presented so that texture parameters are computed only in a neighborhood of salient points. We use Gabor texture features as image content descriptors and efficiently emply them to retrieve images.
Keywords: Gabor filters, image retrieval, texture feature extraction, hierarchical representation

فایل PDF – در ۱۰ صفحه- نویسنده : TOMASZ ANDRYSIAK, MICHAŁ CHORA´ S

IMAGE RETRIEVAL BASED ON HIERARCHICAL GABOR FILTERS

پسورد فایل : behsanandish.com


۷٫Iris Recognition Based On Adaptive Gabor Filter

Abstract. Aiming at the problem of multi-category iris recognition, there proposes a method of iris recognition algorithm based on adaptive Gabor filter. Use DE-PSO to adaptive optimize the Gabor filter parameters. DE-PSO is composed of particle swarm optimization and differential evolution algorithm. Use 16 groups of 2D-Gabor filters with different frequencies and directions to process iris images. According to the direction and frequency of maximum response amplitude, transform iris features into 512-bit binary feature encoding. Calculate the Hamming distance of feature code and compare with the classification threshold, determine iris the type of iris. Experiment on a variety of iris databases with multiple Gabor filter algorithms, the results showed that this algorithm has higher recognition rate, the ROC curve is closer to the coordinate axis and the robustness is better, compare with other Gabor filter algorithm.

Keywords: Iris recognition Gabor filter Particle swarm optimization Differential evolutionFeature encodingHamming distance

 

فایل PDF – در ۸ صفحه- نویسنده : Shuai Liu, Yuanning Liu, Xiaodong Zhu, Guang Huo, Jingwei Cui, and Yihao Chen

 

Iris Recognition Based On Adaptive Gabor Filter

پسورد فایل : behsanandish.com


۸٫USE OF GABOR FILTERS FOR TEXTURE CLASSIFICATION OF AIRBORNE IMAGES AND LIDAR DATA

KEY WORDS: Texture analysis, LIDAR, Algorithm, Urban and Vegetation Detection, Automated Classification
ABSTRACT: In this paper, a texture approach is presented for building and vegetation extraction from LIDAR and aerial images. The texture is very important attribute in many image analysis or computer vision applications. The procedures developed for texture problem can be subdivided into four categories: structural approach, statistical approach, model based approach and filter based approach. In this paper, different definitions of texture are described, but complete emphasis is given on filter based methods. Examples of filtering methods are Fourier transform, Gabor and wavelet transforms. Here, Gabor filter is studied and its implementation for texture analysis is explored. This approach is inspired by a multi-channel filtering theory for processing visual information in the human visual system. This theory holds that visual system decomposes the image into a number of filtered images of a specified frequency, amplitude and orientation.  The main objective of the article is to use Gabor filters for automatic urban object and tree detection. The first step is a definition of Gabor filter parameters: frequency, standard deviation and orientation. By varying these parameters, a filter bank is obtained that covers the frequency domain almost completely. These filters are used to aerial images and LIDAR data. The filtered images that possess  a significant information about analyzed objects are selected, and the rest are discarded.  Then, an energy measure is defined on the filtered images in order to compute different texture features. The Gabor features are used to image segmentation using thresholding.  The tests were performed using set of images containing very different landscapes: urban area and vegetation of varying configurations, sizes and shapes of objects. The performed studies revealed that textural algorithms have the ability to detect buildings and trees. This article is the attempt to use texture methods also to LIDAR data, resampling into regular grid cells. The obtained preliminary results are interesting.

 

فایل PDF – در ۱۲ صفحه- نویسنده : Urszula Marmol


USE OF GABOR FILTERS FOR TEXTURE CLASSIFICATION OF AIRBORNE IMAGES AND LIDAR DATA

پسورد فایل : behsanandish.com

مجموعه مقالات فیلتر گابور (Gabor Filter) قسمت ۱
مجموعه مقالات فیلتر گابور (Gabor Filter) قسمت ۲

یادگیری ماشین – SVM یا ماشین بردار پشتیبان به زبان ساده

یکی از الگوریتم ها و روشهای بسیار رایج در حوزه دسته بندی داده ها، الگوریتم SVM یا ماشین بردار پشتیبان است که در این مقاله سعی شده است به زبان ساده و به دور از پیچیدگیهای فنی توضیح داده شود.

آشنایی با مفهوم دسته بندی

فرض کنید مجموعه داده ای داریم که ۵۰٪ افراد آن مرد و ۵۰٪ افراد آن زن هستند. این مجموعه داده می تواند مشتریان یک فروشگاه آنلاین باشد. با داشتن یک زیرمجموعه از این داده ها که جنسیت افراد در آن مشخص شده است، می خواهیم قوانینی ایجاد کنیم که به کمک آنها جنسیت بقیه افراد مجموعه را بتوانیم با دقت بالایی تعیین کنیم. تشخیص جنسیت بازدیدکنندگان فروشگاه، باعث می شود بتوانیم تبلیغات جداگانه ای را برای زنان و مردان نمایش دهیم و سودآوری فروشگاه را بالا ببریم . این فرآیند را در علم تحلیل داده، دسته بندی می نامیم .

برای توضیح کامل مسأله، فرض کنید دو پارامتری که قرار است جنسیت را از روی آنها تعیین کنیم، قد و طول موی افراد است . نمودار پراکنش قد و طول افراد در زیر نمایش داده شده است که در آن جنسیت افراد با دو نماد مربع (مرد) و دایره (زن) به طور جداگانه نمایش داده شده است .

SVM-1

 

با نگاه به نمودار فوق، حقایق زیر به سادگی قابل مشاهده است :

  1. مردان در این مجموعه، میانگین قد بلندتری دارند.
  2. زنان از میانگین طول موی بیشتری برخوردار هستند.

اگر یک داده جدید با قد ۱۸۰cm و طول موی ۴cm به ما داده شود، بهترین حدس ما برای ماشینی این شخص، دسته مردان خواهد بود .

بردارهای پشتیبان و ماشین بردار پشتیبان

بردارهای پشتیبان به زبان ساده، مجموعه ای از نقاط در فضای n بعدی داده ها هستند که مرز دسته ها را مشخص می کنند و مرزبندی و دسته بندی داده ها براساس آنها انجام می شود و با جابجایی یکی از آنها، خروجی دسته بندی ممکن است تغییر کند . به عنوان مثال در شکل فوق ، بردار (۴۵,۱۵۰) عضوی از بردار پشتیبان و متعلق به یک زن است . در فضای دوبعدی ،‌بردارهای پشتیبان، یک خط، در فضای سه بعدی یک صفحه و در فضای n بعدی یک ابر صفحه را شکل خواهند داد.

SVM یا ماشین بردار پشتیبان ، یک دسته بند یا مرزی است که با معیار قرار دادن بردارهای پشتیبان ، بهترین دسته بندی و تفکیک بین داده ها را برای ما مشخص می کند.

در SVM فقط داده های قرار گرفته در بردارهای پشتیبان مبنای یادگیری ماشین و ساخت مدل قرار می گیرند و این الگوریتم به سایر نقاط داده حساس نیست و هدف آن هم یافتن بهترین مرز در بین داده هاست به گونه ای که بیشترین فاصله ممکن را از تمام دسته ها (بردارهای پشتیبان آنها) داشته باشد .

چگونه یک ماشین بر مبنای بردارهای پشتیبان ایجاد کنیم ؟

به ازای داده های موجود در مثال فوق، تعداد زیادی مرزبندی می توانیم داشته باشیم که سه تا از این مرزبندی ها در زیر نمایش داده شده است.

 

SVM-2

 

سوال اینجاست که بهترین مرزبندی در این مسأله کدام خط است ؟

یک راه ساده برای انجام اینکار و ساخت یک دسته بند بهینه ، محاسبه فاصله ی مرزهای به دست آمده با بردارهای پشتیبان هر دسته (مرزی ترین نقاط هر دسته یا کلاس) و در نهایت انتخاب مرزیست که از دسته های موجود، مجموعاً بیشترین فاصله را داشته باشد که در شکل فوق خط میانی ، تقریب خوبی از این مرز است که از هر دو دسته فاصله ی زیادی دارد. این عمل تعیین مرز و انتخاب خط بهینه (در حالت کلی ، ابر صفحه مرزی) به راحتی با انجام محاسبات ریاضی نه چندان پیچیده قابل پیاده سازی است .

توزیع غیر خطی داده ها و کاربرد ماشین بردار پشتیبان

اگر داده ها به صورت خطی قابل تفکیک باشند، الگوریتم فوق می تواند بهترین ماشین را برای تفکیک داده ها و تعیین دسته یک رکورد داده، ایجاد کند اما اگر داده ها به صورت خطی توزیع شده باشند (مانند شکل زیر )، SVM را چگونه تعیین کنیم ؟

 

SVM-3

 

در این حالت، ما نیاز داریم داده ها را به کمک یک تابع ریاضی (Kernel functions) به یک فضای دیگر ببریم (نگاشت کنیم ) که در آن فضا، داده ها تفکیک پذیر باشند و بتوان SVM آنها را به راحتی تعیین کرد. تعیین درست این تابع نگاشت در عملکرد ماشین بردار پشتیبان موثر است که در ادامه به صورت مختصر به آن اشاره شده است.

با فرض یافتن تابع تبدیل برای مثال فوق،‌ فضای داده ما به این حالت تبدیل خواهد شد :

 

SVM-4

 

در این فضای تبدیل شده، یافتن یک SVM به راحتی امکان پذیر است .

نگاهی دقیق تر به فرآیند ساخت SVM

همانطور که اشاره شد،‌ماشین بردار پشتیبان یا SVM داده ها را با توجه به دسته های از پیش تعیین شده آنها به یک فضای جدید می برد به گونه ای که داده ها به صورت خطی (یا ابر صفحه ) قابل تفکیک و دسته بندی باشند و سپس با یافتن خطوط پشتیبان (صفحات پشتیبان در فضای چند بعدی) ، سعی در یافتن معادله خطی دارد که بیشترین فاصله را بین دو دسته ایجاد می کند.

در شکل زیر داده ها در دو دوسته آبی و قرمز نمایش داده شده اند و خطوط نقطه چین ، بردار های پشتیبان متناظر با هر دسته را نمایش می دهند که با دایره های دوخط مشخص شده اند و خط سیاه ممتد نیز همان SVM است . بردار های پشتیبان هم هر کدام یک فرمول مشخصه دارند که خط مرزی هر دسته را توصیف می کند.

SVM-5

SVM‌ در پایتون

برای استفاده از ماشین بردار پشتیبان در پایتون، توصیه بنده استفاده از کتابخانه یادگیری ماشین پایتون به نام scikitlearn است که تمام کرنل ها و توابع نگاشت را به صورت آماده شده دارد. سه تا تابعSVC , NuSVC , LinearSVC وظیفه اصلی دسته بندی را برعهده دارند . (SVC = Support Vector Classifier) . نمونه ای از دسته بندی با این توابع را در زیر می توانید مشاهده کنید :

 

SVM-6

ماشین بردار پشتیبانی در عمل

برای استفاده از SVM در مورد داده های واقعی ، چندین نکته را باید رعایت کنید تا نتایج قابل قبولی را بگیرید

  1. ابتدا داده ها را پالایش کنید (نقاط پرت ،‌ داده های ناموجود و …..)
  2. داده را عددی و نرمال کنید . این مباحث را در مقالات پیش پردازش داده ها دنبال کنید. به طور خلاصه ، داده هایی مانند جنسیت، رشته تحصیلی و … را به عدد تبدیل کنید و سعی کنید مقادیر همه صفات بین یک تا منهای یک [۱,-۱] نرمال شوند تا بزرگ یا کوچک بودن مقادیر یک ویژگی داده ها،‌ ماشین را تحت تاثیر قرار ندهد .
  3. کرنل های مختلف را امتحان و به ازای هر کدام، با توجه به مجموعه داده آموزشی که در اختیار دارید و دسته بندی داده های آنها مشخص است، دقت SVM را اندازه گیری کنید و در صورت نیاز پارامتر های توابع تبدیل را تغییر دهید تا جواب های بهتری بگیرید. این کار را برای کرنل های مختلف هم امتحان کنید . می توانید از کرنل RBF شروع کنید .

نقاط ضعف ماشین بردار پشتیان

  • این نوع الگوریتم ها، محدودیت های ذاتی دارند مثلا هنوز مشخص نشده است که به ازای یک تابع نگاشت ، پارامترها را چگونه باید تعیین کرد.
  • ماشینهای مبتنی بر بردار پشتیبان به محاسبات پیچیده و زمان بر نیاز دارند و به دلیل پیچیدگی محاسباتی، حافظه زیادی نیز مصرف می کنند.
  • داده های گسسته و غیر عددی هم با این روش سازگار نیستند و باید تبدیل شوند.

با این وجود، SVM‌ ها دارای یک شالوده نظری منسجم بوده و جواب های تولید شده توسط آنها ، سراسری و یکتا می باشد. امروزه ماشینهای بردار پشتیبان، به متداول ترین تکنیک های پیش بینی در داده کاوی تبدیل شده اند.

سخن پایانی

ماشینهای بردار پشتیبان، الگوریتم های بسیار قدرتمندی در دسته بندی و تفکیک داده ها هستند بخصوص زمانی که با سایر روشهای یادگیری ماشین مانند روش جنگل تصادفی تلفیق شوند. این روش برای جاهایی که با دقت بسیار بالا نیاز به ماشینی داده ها داریم، به شرط اینکه توابع نگاشت را به درستی انتخاب کنیم، بسیار خوب عمل می کند .

ساختار اصلی این نوشتار از روی یک مقاله سایت آنالیتیکزویدیا برداشته شده است و برای دو بخش پایانی مقاله هم از کتاب «داده کاوی پیشرفته : مفاهیم و الگوریتم ها» دکتر شهرابی استفاده شده است .

منبع


آشنایی با ماشین بردار پشتیبان (SVM) – مرور کلی

SVM یک مدل یادگیری نظارت شده است.

پس قبل از این که به سراغ آن برویم باید یک مجموعه داده(Dataset) که از قبل برچسب‌گذاری شده(Labeled) را داشته باشیم.

مثالفرض کنیم من صاحب یک کسب‌وکار هستم و هر روز تعداد زیادی ایمیل از مشتری‌ها دریافت می‌کنم. بعضی از این ایمیل‌ها شکایت‌ها و نارضایتی‌هایی هستند که من هرچه سریع‌تر باید به آن‌ها پاسخ بدهم و به آن‌ها رسیدگی کنم. در غیر این صورت کسب‌وکار من با ضرر روبرو خواهد شد.

من به دنبال راهی هستم که این ایمیل‌ها را هرچه سریع‌تر تشخیص بدهم(پیدا کنم) و پاسخ آن‌ها را زودتر از بقیه ارسال کنم.

رویکرد اولمن می‌توانم برچسب‌هایی با عنوان‌های: اورژانسی، شکایت و راهنمایی در جیمیل(GMail) خود ایجاد کنم.

اشکال این روش این است که من باید مدتی فکر کنم و همه کلمه‌های کلیدی(Keyword) بالغوه که مشتری‌های عصبانی ممکن است در ایمیل‌های خود استفاده کنند را پیدا کنم. طبیعی است که بعضی از آن‌ها را هم از قلم انداخته شوند. با گذشت زمان هم لیست این کلمه‌ها به احتمال زیاد شلوغ و مدیریت کردن آن‌ها به کار مشکلی تبدیل می‌شود.

رویکرد دوممن می‌توانم از یک الگوریتم یادگیری ماشین نظارت شده استفاده کنم.

قدم اولبه تعدادی ایمیل نیاز دارم.(هرچه بیشتر بهتر)

قدم دومعنوان ایمیل‌های قدم اول رو می‌خوانم و آن‌ها را در یکی از دو گروه «شکایت است» و یا «شکایت نیست» طبقه‌بندی می‌کنم. اینجوری می‌توانم ایمیل‌ها را برچسب ‌گذاری کنم.

قدم سومروی این مجموعه داده، مدلی را آموزش می‌دهم.

قدم چهارمکیفیت یا صحت پیش‌بینی های مدل آموزش داده‌شده را ارزیابی می‌کنم.(با استفاده از روش Cross Validation)

قدم پنجماز این مدل برای پیش‌بینی این که ایمیل‌های جدیدی که رسیده‌اند، شکایت هستند یا نه، استفاده می‌کنم.

در این رویکرد اگر مدل را با تعداد ایمیل‌های زیادی آموزش داده باشیم، مدل عملکرد خوبی را نشون می‌دهد. SVM فقط یکی از روش‌هایی هست که ما می‌توانیم برای یادگرفتن از داده‌های موجود و پیش‌بینی کردن، استفاده کنیم.

همچنین باید به این نکته هم توجه داشته باشیم که قدم دوم اهمیت زیادی دارد و دلیلش این است که اگر در شروع کار، ایمیل‌های برچسب‌گذاری نشده را به SVM بدهیم، کار خاصی را نمیتواند انجام دهد.

SVM یک مدل خطی را یاد می‌گیرد

در مثال قبل دیدیم که در قدم سوم یک الگوریتم یادگیری نظارت شده مثل SVM به کمک داده‌هایی که از قبل برچسب‌گذاری شده‌اند آموزشداده شد. اما برای چه چیزی آموزش داده شد؟ برای این که چیزی را یاد بگیرد.

چه چیزی را یاد بگیرد؟

در مورد SVM، یک مدل خطیرا یاد میگیرد.

مدل خطی چیست؟ اگر بخواهیم به زبان ساده بیان کنیم یک خط است.(و در حالت پیچیده‌تر یک ابر صفحه).

اگر داده‌های شما خیلی ساده و دو بعدی باشند، در این صورت SVM خطی را یاد می‌گیرد که آن خط می‌تواند داده‌ها را به دو بخش تقسیم کند.

 

svm

SVM قادر است که خطی را پیدا کند که داده‌ها را جدا می‌کند.

 

خب پس اگر SVM فقط یک خط است، پس چرا ما داریم راجع به مدل خطی صحبت می‌کنیم؟

برای این که ما همینطوری نمی‌توانیم به یک خط چیزی را آموزش بدهیم.

در عوض:

  1. در نظر می‌گیریم که داده‌هایی که می‌خواهیم طبقه‌بندی کنیم، می‌توانند به وسیله یک خط از هم تفکیک شوند.
  2. می‌دانیم که یک خط می‌تواند به کمک معادله y=wx+by=wx+b نمایش داده شود.(این همان مدل ما است)
  3. می‌دانیم با تغییر دادن مقدار w و b بی‌نهایت خط وجود خواهد داشت.
  4. برای تعیین این که کدام مقدار w و b بهترینخط جداکننده داده‌ها را به ما می‌دهد، از یک الگوریتم استفاده می‌کنیم.

SVM یکی از این الگوریتم‌ها هست که می‌تواند این کار را انجام دهد.

الگوریتم یا مدل؟

در شروع این پست من نوشتم که SVM یک مدل یادگیری نظارت شده است، و الآن می‌نویسم که آن یک الگوریتم است. چه شده؟ از واژه الگوریتم معمولا آزادانه استفاده می‌شود. برای نمونه، ممکن است که شما جایی بخوانید یا بشنوید که  SVM یک الگوریتم یادگیری نظارت شده است. اگر این نکته را در نظر بگیریم که الگوریتم مجموعه‌ای از فعالیت‌ها است که انجام می‌شوند تا به نتیجه مشخصی دست یابیم، می‌بینیم که استفاده از این واژه در اینجا صحیح نیست(منظور از واژه الگوریتم اینجا الگوریتمی است که برای آموزش از آن استفاده می‌کنیم). بهینه‌سازی متوالی کمینه(Sequential minimal optimization) پر استفاده ترین الگوریتم برای آموزش SVM است. با این حال می‌توان از الگوریتم‌های دیگری مثل کاهش مختصات(Coordinate descent) هم استفاده کرد. در کل بیشتر به جزییاتی مثل این علاقمند نیستند، در نتیجه ما هم برای ساده‌تر شدن فقط از واژه الگوریتم SVM استفاده می‌کنیم(بدون ذکر جزییات الگوریتم آموزشی که استفاده می‌کنیم).

SVM یا SVMها؟

بعضی وقت‌ها می‌بینیم که مردم راجع به SVM و بعضی وقت‌ها هم راجع به SVMها صحبت می‌کنند.

طبق معمول ویکی‌پدیا در روشن و شفاف کردن چیزها به ما کمک می‌کند:

در یادگیری ماشینی، ماشین‌های بردار پشتیبان (SVMsمدل‌های یادگیری نظارت شده به همراه الگوریتم‌های آموزش مربوطههستندکه در تحلیل داده‌های استفاده شده در  رگرسیون و طبقه‌بندی از آن‌ها استفاده می‌شود.(ویکی‌پدیا)

پس حالا ما این را می‌دانیم که چندین مدل‌ متعلق به خانواده SVM وجود دارند.

SVMها – ماشین‌های بردار پشتیبان

بر اساس ویکی‌پدیا SVMها همچنین می‌توانند برای دو چیز استفاده شوند، طبقه‌بندی و رگرسیون.

  • SVM برای طبقه‌بندی استفاده می‌شود.
  • SVR یا(Support Vector Regression) برای رگرسیون.

پس گفتن ماشین‌های بردار پشتیبان هم دیگه الآن منطقی به نظر میاد. با این وجود این پایان داستان نیست!

طبقه‌بندی

در سال ۱۹۵۷ یک مدل خطی ساده به نام پرسپترون توسط فردی به نام فرانک روزنبلت برای طبقه‌بندی اختراع شد(که در واقع اساس شبکه‌های عصبی ساده‌ای به نام پرسپترون چند لایه است).

چند سال بعد، واپنیک و چروننکیس مدل دیگری به نام «طبقه‌بندی کننده حداکث حاشیه» پیشنهاد دادند و همان‌جا بود که SVM متولد شد.

در سال ۱۹۹۲ واپنیک و همکارانش ایده‌ای داشتند که یک چیزی به نام کلک کرنل(Kernel Trick) را به روش قبلی اضافه کنند تا به آن‌ها اجازه دهد که حتی داده‌هایی که به صورت خطی تفکیک‌پذیر نیستند را هم طبقه‌بندی کنند.

سرانجام در سال ۱۹۹۵، کورتز و واپنیک، طبقه‌بندی کننده حاشیه نرم را معرفی کردند که به SVM اجازه می‌دهد تا بعضی از اشتباهات در طبقه‌بندی را هم بپذیرد.

پس وقتی که ما از طبقه‌بندی صحبت می‌کنیم، چهار ماشین بردار پشتیبان مختلف وجود دارد.

  1. طبقه‌بندی کننده حاشیه حداکثر.
  2. نسخه‌ای که از کلک کرنل استفاده می‌کند.
  3. نسخه‌ای که از حاشیه نرم استفاده می‌کند.
  4. نسخه‌ای که ترکیب همه موارد قبلی است.

و البته آخرین روش معمولا بیشترین کاربرد را دارد. دلیل اصلی این که قهمیدن SVMها در نگاه اول کمی گیج کننده به نظر می‌رسد هم همین موضو ع است که ‌آن‌ها از چندین قطعه تسکیل شده اند که در طول زمان به ‌‌آن‌ها چیزهایی اضافه شده است.

به همین دلیل است که وقتی از یک زبان برنامه‌‌نویسی استفاده می‌کنید می‌پرسید از کدام کرنل باید استفاده کنیم(بخاطر کرنل‌های مختلفی که وجود دارند) و یا کدام مقدار ابرپارامتر C را باید استفاده کنید(برای کنترل تاثیر حاشیه نرم).

رگرسیون

در سال ۱۹۹۶، واپنیک و همکارانش، نسخه‌ای از SVM را پیشنهاد دادند که به جای طبقه‌بندی، عمل رگرسیون را انجام می‌دهد. این مورد به Support Vector Regression یا SVR معروف است. همانند SVM در این مدل نیز از کلک کرنل و ابرپارامتر C  استفاده می‌شود.

در آینده مقاله ساده‌ای در مورد توضیح چگونگی استفاده از  SVR در زبان R خواهم نوشت و آدرس آن را همین‌جا قرار خواهم داد.

اگر علاقمند هستید که راجع به SVR بیشتر بدانیند، می‌توانید به این آموزش خوب که نوشته Smola and Schölkopft است، مراجعه کنید.

خلاصه تاریخچه

  • طبقه‌بندی کننده حاشیه حداکثر (۱۹۶۳ یا ۱۹۷۹)
  • کلک کرنل (۱۹۹۲)
  • طبقه‌بندی کننده حاشیه نرم (۱۹۹۵)
  • رگرسیون بردار پشتیبان (۱۹۹۶)

در صورتی که مایلید بیشتر راجع به تاریخچه بدانید، می‌توانید به مقاله مرور همراه با جزییات از تاریخچه مراجعه کنید.

انواع دیگری از ماشین‌های بردار پشتیبان

به دلیل این که SVMها در طبقه‌بندی خیلی موفق بودند، مردم شروع به فکر کردن راجع به این موضوع کردند که چطور می‌توانند از همین منطق در انواع دیگر مسائل استفاده کنند یا این‌ که چطور مشتقات آن را ایجاد کنند. در نتیجه چندین روش مختلف و جالب در خانواده SVM به وجود آمد.

  • ماشین بردار پشتیبان ساخت‌یافتهکه توانایی پیش‌بینی اشیای ساخت‌یافته را دارد.
  • ماشین بردار پشتیبان حداقل مربعکه در طبقه‌بندی و رگرسیون استفاده می‌شود.
  • خوشه‌بندی بردار پشتیبانکه در تحلیل خوشه استفاده می‌شود.
  • ماشین بردار پشتیبان هدایتیکه در یادگیری نیمه نظارت‌شده استفاده می‌شود.
  • SVM رتبه‌بندیبرای مرتب کردن نتایج.
  • ماشین بردار پشتیبان تک کلاسهکه برای تشخیص ناهنجاری استفاده می‌شود.

نتیجه‌گیری

دیدیم که سختی در درک کردن این که SVMها دقیقا چه چیزی هستند، امری طبیعی است. علتش هم این است که چندین SVM برای چندین منظور وجود دارند. مثل همیشه تاریخ به ما اجازه می‌دهد که دید بهتری راجع به چگونگی به وجود آمدن SVMهایی که امروزه وجود دارند، داشته باشیم.

امیدوارم این مقاله دید وسیع‌تری از چشم‌انداز  SVM به شما داده باشد و کمک کرده باشد که بهتر این ماشین‌ها را بشناسید و درک کنید.

اگه مایلید که بیشتر راجع به نحوه کار SVM در طبقه‌بندی بدانید، می‌توانید به آموزش‌های ریاضی مربوط به آن مراجعه کنید.

منبع

 

ماشین بردار پشتیبان (svm) قسمت ۱
ماشین بردار پشتیبان (svm) قسمت ۲
ماشین بردار پشتیبان (svm) قسمت ۳

شبکه عصبی مصنوعی به زبان ساده

یک شبکه عصبی مصنوعی (Artificial Neural Network – ANN) ایده ای برای پردازش اطلاعات است که از سیستم عصبی زیستی الهام گرفته و مانند مغز به پردازش اطلاعات می‌پردازد. عنصر کلیدی این ایده، ساختار جدید سیستم پردازش اطلاعات است. این سیستم از شمار زیادی عناصر پردازشی فوق العاده بهم پیوسته به نام نورون‌ها (neurons) تشکیل شده که برای حل یک مسئله با هم هماهنگ عمل می‌کنند.

شبکه های عصبی مصنوعی نیز مانند انسان‌ها با مثال یاد می گیرند و یک شبکه عصبی برای انجام وظیفه‌های مشخص مانند شناسایی الگوها و دسته بندی اطلاعات، در طول یک پروسه یاد گیری تنظیم می‌شود. در سیستم‌های زیستی، یاد گیری با تنظیماتی در اتصالات سیناپسی که بین اعصاب قرار دارد همراه است. از این روش در شبکه های عصبی نیز استفاده می‌شود.

شبکه های عصبی مصنوعی (ANN) که به اختصار به آن شبکه عصبی نیز گفته می‌شود، نوع خاصی از مدل یادگیری است که روش کارکرد سیناپس‌ها در مغز انسان را تقلید می‌کند.

شبکه های عصبی مصنوعی با پردازش داده‌های تجربی، دانش یا قانون نهفته در ورای داده‌ها را به ساختار شبکه منتقل می‌کند که به این عمل یادگیری می‌گویند. اصولاً توانایی یادگیری مهمترین ویژگی یک سیستم هوشمند است. سیستمی کهقابلیت یادگیری داشته باشد، منعطف تر است وساده تر برنامه‌ریزی می‌شود، بنابراین بهتر می‌تواند در مورد مسایل و معادلات جدید پاسخگو باشد.

ساختار شبکه عصبی

انسان‌ها از زمان‌های بسیار دور سعی بر آن داشتند که بیوفیزیولوژی مغز را دریابند زیرا که همواره مسئله هوشمندی انسان و قابلیت یادگیری، تعمیم، خلاقیت، انعطاف پذیری و پردازش موازی در مغز برای بشر جالب بوده و بکارگیری این قابلیت‌ها در ماشین‌ها بسیار مطلوب می‌نمود. روش‌های الگوریتمیک برای پیاده سازی این خصایص در ماشین‌ها مناسب نمی‌باشند، در نتیجه می‌بایست روش‌هایی مبتنی بر همان مدل‌های بیولوژیکی ابداع شوند.

شبکه عصبی-05

 

به عبارت دیگر شبکه‌ی عصبی  یک سامانه پردازش داده‌ها است که از مغز انسان ایده گرفته و پردازش داده‌ها را به عهدهپردازنده‌های کوچک و بسیار زیادی می‌سپارد که به صورت شبکه‌ای به هم پیوسته و موازی با یکدیگر برای حل یک مسئله رفتار می‌کنند. در این شبکه‌ها به کمک دانش برنامه نویسی، ساختار داده‌ای طراحی می‌شود که می‌تواند همانندنورون عمل کند. به این ساختار داده گره گفته می‌شود.

در این ساختار با ایجاد شبکه‌ای بین این گره‌ها و اعمال یک الگوریتم آموزشی به آن، شبکه را آموزش می‌دهند. در این حافظه یا شبکه عصبی گره‌ها دارای دو حالت فعال (روشن یا ۱) و غیرفعال (خاموش یا ۰) اند و هر یال (سیناپس یا ارتباط بین گره‌ها) دارای یک وزن می‌باشد. یال‌های با وزن مثبت، موجب تحریک یا فعال کردن گره غیر فعال بعدی می‌شوند و یال‌های با وزن منفی، گره متصل بعدی را غیر فعال یا مهار (در صورتی که فعال بوده باشد) می‌کنند.

مثالی برای  شبکه عصبی

در روش‌های محاسباتی سنتی، از یک سری عبارات منطقی برای اجرای یک عمل استفاده می‌شود؛ اما در مقابل، شبکه های عصبی از مجموعه نودها (به عنوان نرون) و یال‌ها (در نقش سیناپس) برای پردازش داده بهره می‌گیرند. در این سیستم، ورودی‌ها در شبکه به جریان افتاده و یک سری خروجی تولید می‌گردد.

 

شبکه عصبی-02

 

سپس خروجی‌ها با داده‌های معتبر مقایسه می‌گردند. مثلا فرض کنید می‌خواهید کامپیوتر خود را به گونه‌ای آموزش دهید که تصویر گربه را تشخیص دهد. برای این کار میلیون‌ها تصویر از گربه‌های مختلف را وارد شبکه کرده و آنهایی که از سوی سیستم به عنوان خروجی انتخاب می‌شوند را دریافت می‌کنید.

در این مرحله کاربر انسانی می‌تواند به سیستم بگوید که کدام یک از خروجی‌ها دقیقا تصویر گربه هستند. بدین ترتیب مسیرهایی که به تشخیص موارد درست منجر شده، از طرف شبکه تقویت خواهند شد. با تکرار این فرایند در دفعات زیاد، شبکه نهایتا قادر است به دقت بسیار خوبی در اجرای وظیفه موردنظر دست یابد.

البته شبکه های عصبی را نمی‌توان پاسخ تمام مسائل محاسباتی پیش روی انسان دانست، اما در مواجهه با داده‌های پیچیده، بهترین گزینه به شمار می‌روند.

اخیرا گوگل و مایکروسافت هر دو اعلام کردند یادگیری مبتنی بر شبکه های عصبی را به نرم‌افزار‌های مترجمشان افزوده‌اند.

گوگل و مایکروسافت از شبکه های عصبی برای تقویت اپلیکیشن‌های ترجمه خود بهره گرفته‌اند و به نتایج بسیار خوبی دست یافته‌اند، زیرا عمل ترجمه از جمله فرایندهای بسیار پیچیده محسوب می‌گردد.

شبکه عصبی-ترجمه

 

بدین ترتیب با استفاده از قابلیت یادگیری شبکه های عصبی، سیستم ترجمه می‌تواند ترجمه‌های صحیح را برای یادگیری به کار گرفته و به مرور زمان به دقت بیشتری دست یابد.

چنین وضعیتی در تشخیص گفتار نیز به وجود آمد. پس از افزودن یادگیری با شبکه های عصبی در Google Voice نرخ خطای این برنامه تا ۴۹% کاهش یافت. البته این قابلیت هیچوقت بدون نقص نخواهد بود، اما به مرور زمان شاهد پیشرفت آن هستیم.

در مجموع با به کار گیری روش‌های یادگیری مبتنی بر شبکه های عصبی، آنالیز داده‌های پیچیده روز به روز پیشرفت می‌کند و در نهایت به قابلیت‌های انعطاف پذیر‌تری در نرم‌افزار‌ها و کاربرد آن‌ها در زندگی روزانه دست خواهیم یافت.

شبکه های عصبی در مقابل کامپیوتر های معمولی

شبکه های عصبی نسبت به کامپیوتر‌های معمولی مسیر متفاوتی را برای حل مسئله طی می‌کنند. کامپیوتر‌های معمولی یکمسیر الگوریتمی را استفاده می‌کنند به این معنی که کامپیوتر یک مجموعه از دستورالعمل‌ها را به قصد حل مسئله پی می‌گیرد. اگر قدم‌های خاصی که کامپیوتر باید بردارد، شناخته شده نباشند، کامپیوتر قادر به حل مسئله نخواهد بود. این حقیقت قابلیت حل مسئله‌ی کامپیوترهای معمولی را به مسائلی محدود می‌کند که ما قادر به درک آن‌ها هستیم و می دانیم چگونه حل می‌شوند.

از طرف دیگر، کامپیوترهای معمولی از یک مسیر مشخص برای حل یک مسئله استفاده می‌کنند. راه حلی که مسئله از آن طریق حل می‌شود باید از قبل شناخته شده و به صورت دستورات کوتاه و غیر مبهمی شرح داده شده باشد. این دستورات به زبان‌های برنامه نویسی سطح بالا برگردانده شده و سپس به کدهایی قابل درک و پردازش برای کامپیوترها تبدیل می‌شوند.

شبکه های عصبی اطلاعات را به روشی مشابه با کاری که مغز انسان انجام می‌دهد پردازش می‌کنند. آن‌ها از تعداد زیادی ازعناصر پردازشی (سلول عصبی) که فوق العاده بهم پیوسته‌اند تشکیل شده است که این عناصر به صورت موازی باهم برای حل یک مسئله مشخص کار می‌کنند. شبکه های عصبی با مثال کار می‌کنند و نمی‌توان آن‌ها را برای انجام یک وظیفه خاص برنامه ریزی کرد. مثال‌ها می‌بایست با دقت انتخاب شوند در غیر این صورت باعث اتلاف وقت و هزینه می‌شود و حتی بدتر از آن، ممکن است شبکه درست کار نکند.

امتیاز شبکه عصبی در آن است که چگونگی حل مسئله را خودش کشف می‌کند!

شبکه های عصبی و کامپیوتر‌های معمولی با هم در حال رقابت نیستند بلکه کامل کننده یکدیگرند. انجام بعضی وظایف مانند عملیات‌های محاسباتی بیشتر مناسب روش‌های الگوریتمی است. همچنین انجام برخی دیگر از وظایف که به یادگیریو آزمون و خطا نیاز دارند را بهتر است به شبکه های عصبی بسپاریم. فراتر که می‌رویم، مسائلی وجود دارد که به سیستمیترکیبی از روش های الگوریتمی و شبکه های عصبی برای حل آن‌ها مورد نیاز است (بطور معمول کامپیوتر های معمولی برای نظارت بر شبکه های عصبی به کار گرفته می‌شوند ) به این منظور که بیشترین کارایی بدست آید.

 

شبکه عصبی-03

مزیت‌های شبکه عصبی

شبکه عصبی با قابلیت قابل توجه آن‌ها در جست و جو معانی از داده‌های پیچیده یا مبهم، می‌تواند برای استخراج الگوها و شناسایی روش‌هایی که آگاهی از آن‌ها برای انسان و دیگر تکنیک‌های کامپیوتری بسیار پیچیده و دشوار است به کار گرفته شود. یک شبکه عصبی تربیت یافته می‌تواند به عنوان یک متخصص در مقوله اطلاعاتی که برای تجزیه تحلیل به آن داده شده به حساب آید.

شبکه های عصبی معجزه نمی‌کنند اما اگر خردمندانه به کار گرفته شوند نتایج شگفت آوری خلق می‌کنند.

مزیت‌های دیگر شبکه های عصبی

  • یادگیری انطباق پذیر (Adaptive Learning)

یادگیری انطباق پذیر، قابلیت یادگیری و نحوه انجام وظایف بر پایه اطلاعات داده شده برای تمرین و تجربه‌های مقدماتی.

سازماندهی توسط خود یعنی یک شبکه هوش مصنوعی سازماندهی یا ارائه‌اش را برای اطلاعاتی که در طول دوره یادگیری دریافت می‌کند، خودش ایجاد کند.

در عملکرد بهنگام محاسبات شبکه هوش مصنوعی می‌تواند بصورت موازی انجام شود  و سخت افزارهای مخصوصی طراحی و ساخته شده‌ که می‌تواند از این قابلیت استفاده کنند.

خرابی جزئی یک شبکه منجر به تنزل کارایی متناظر با آن می‌شود، اگر چه تعدادی از قابلیت‌های شبکه حتی با خسارت بزرگی هم به کار خود ادامه می‌دهند.

ما شبکه های عصبی را با تلاش اولیه در جهت یافتن خصوصیات اساسی سلول‌های عصبی و اتصالات آن‌ها هدایت می‌کنیم. سپس بطور معمول یک کامپیوتر را برای شبیه سازی این خصوصیات برنامه‌ریزی می‌کنیم. اگر چه بدلیل اینکه دانش ما از سلول‌های عصبی ناقص است و قدرت محاسبات ما محدود است، مدل‌های ما لزوما آرمان‌های خام و ناقصی از شبکه‌های واقعی سلول‌های عصبی است.

انواع شبکه عصبی مصنوعی

شبکه‌های پیش خور، شبکه‌هایی هستند که مسیر پاسخ در آن‌ها همواره رو به جلو پردازش شده و به نرون‌های لایه‌های قبل خود باز نمی‌گردد. در این نوع شبکه‌ به سیگنال‌ها تنها اجازه عبور از مسیر یکطرفه (از ورودی تا خروجی) داده می‌شود. بنابراین بازخورد یا فیدبک وجود ندارد به این معنی که خروجی هر لایه تنها بر لایه بعد اثر میگذارد و در لایه‌ی خودش تغییری ایجاد نمی‌کند.

شبکه عصبی-شبکه عصبی-پیش‌خور-01

 

تفاوت شبکه هاِی پس خور با شبکه‌های پیش خور در آن است که در شبکه‌های برگشتی حداقل یک سیگنال برگشتی از یک نرون به همان نرون یا نرون‌های همان لایه یا نرون‌های لایه‌های قبل وجود دارد و اگر نرونی دارای فیدبک باشد بدین مفهوم است که خروجی نرون در لحظه حال نه تنها به ورودی در آن لحظه بلکه به مقدار خروجی خود نرون در لحظه ی گذشته نیز وابسته است.

شبکه عصبی-پس خور

 

 یادگیری در شبکه‌ های عصبی

در یادگیری با ناظر  به الگوریتم یادگیری، مجموعه ای از زوج داده‌ها داده می‌شود. هر داده یادگیری شامل ورودی به شبکه و خروجی هدف است. پس از اعمال ورودی به شبکه، خروجی شبکه با خروجی هدف مقایسه می‌گردد و سپس خطای یادگیری محاسبه شده و از آن جهت تنظیم پارامترهای شبکه (وزن ها)، استفاده می‌گردد. به گونه ای که اگر دفعه بعد به شبکه همان ورودی را دادیم، خروجی شبکه به خروجی هدف نزدیک گردد.

یادگیری تشدیدی حالت خاصی از یادگیری با ناظر و یک یادگیری بر‌خط (On-Line) از یک نگاشت ورودی-خروجی است. این کار از طریق یک پروسه سعی و خطا به صورتی انجام می‌پذیرد که شاخصی موسوم به سیگنال تشدید، ماکزیمم شود که در آن بجای فراهم نمودن خروجی هدف، به شبکه عددی که نشان‌دهنده میزان عملکرد شبکه است ارائه می‌گردد.

در یادگیری بدون ناظر یا یادگیری خود سامانده، پارامترهای شبکه عصبی تنها توسط پاسخ سیستم اصلاح و تنظیم می‌شوند. به عبارتی تنها اطلاعات دریافتی از محیط به شبکه را بردارهای ورودی تشکیل می‌دهند.

همان طور که متوجه شدید شبکه عصبی از مهمترین گرایش‌های هوش مصنوعی ، علمی رو‌به رشد و در حال پیشرفت است و شرکت‌های بزرگ نظیر گوگل و مایکروسافت از آن در نرم‌افزارهای خود استفاده می‌کنند.

ساختار شبکه های عصبی مصنوعی به زبان ساده (Artificial Neural Network)

 

ساختار شبکه‌های عصبی مصنوعی به زبان ساده (Artificial Neural Network)

 

شبکه عصبی مصنوعی روشی عملی برای یادگیری توابع گوناگون نظیر توابع با مقادیر حقیقی، توابع با مقادیر گسسته و توابع با مقادیر برداری می‌باشد.

مطالعه شبکه های عصبی مصنوعی تا حد زیادی ملهم از سیستم های یادگیر طبیعی است که در آنها یک مجموعه پیچیده از نرونهای به هم متصل درکار یادگیری دخیل هستند.

گمان می‌رود که مغز انسان از تعداد ‌۱۰۱۱ نرون تشکیل شده باشد که هر نرون با تقریبا ۱۰۴ نرون دیگر در ارتباط است. سرعت سوئیچنگ نرونها در حدود ۳۱۰ ثانیه است که در مقایسه با کامپیوترها ۱۰۱۰ ثانیه  بسیار ناچیز می‌نماید. با این وجود آدمی قادر است در ۰٫۱ ثانیه  تصویر یک انسان را بازشناسائی نماید. ولی برای کامپیتر دقایقی طول می کشد که این بازشناسی انجام شود.

شاید بد نباشد ابتدا به این سوال فکر کنید، چرا با اینکه سرعت سوئیچنگ نرونهای کامپیوتر از مغز انسان بیشتر است ولی انسان‌ها سریعتر چهره یک شخص را به یاد می‌آورند؟

ساختار شبکه عصبی

 

 

هر دو تصویر بالا را مشاهده کنید. چه شباهت‌هایی می‌بینید؟

همانطور که ملاحظه می کنید، تصویر اول یک نرون طبیعی بیولوژیکی است. اطلاعات از طریق ورودی یا همان دندریت وارد نرون می شوند، همان ورودی‌ها در تصویر دوم با مقادیر (x1,…….,xm) قابل مشاهده هستند. در مدل شبکه عصبی مصنوعی به هر ورودی یک وزن (w1,…….,wm) اختصاص می دهیم. این وزن‌ها در واقع اهمیت ورودی‌ها برای ما هستند، یعنی هر چه وزن بیشتر باشد، ورودی برای آموزش شبکه مهمتر است. سپس تمامی ورودی‌ها با هم جمع (Σ) شده و به صورت یک‌لایه به آکسون وارد می شوند. در مرحله بعد Activation Function را بر روی داده‌ها اعمال می‌کنیم.

Activation Function در واقع نسبت به نیاز مسئله و نوع شبکه عصبی ما (در آموزش های بعدی به آن می پردازیم) تعریف می شود. این function شامل یک فرمول ریاضی برای بروزرسانی وزن‌ها در شبکه است.

پس از انجام محاسبات در این مرحله اطلاعات ما از طریق سیناپس های خروجی وارد نرون دیگر می‌شوند، و این مرحله تا جایی ادامه پیدا می‌کند که شبکه اصطلاحا train شده باشد.

منبع

شبکه های عصبی مصنوعی چیست؟ قسمت ۱
شبکه های عصبی مصنوعی چیست؟ قسمت ۲
شبکه های عصبی مصنوعی چیست؟ قسمت ۳
شبکه های عصبی مصنوعی چیست؟ قسمت ۴