cover

پیاده‌سازی و تحلیل عملی الگوریتم DIANA در Python

1. مقدمه

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

از آنجا که در کتابخانه‌های رایج Python مانند scikit-learn پیاده‌سازی مستقیم و رسمی از الگوریتم DIANA ارائه نشده است، پیاده‌سازی این الگوریتم از پایه، ارزش آموزشی بالایی دارد. این کار امکان درک دقیق مراحل اصلی الگوریتم را فراهم می‌کند، از جمله:

  • تشکیل ماتریس فاصله یا عدم‌شباهت؛
  • انتخاب ناهمگن‌ترین خوشه برای تقسیم؛
  • شناسایی عضو آغازگر گروه جداشونده (Splinter Group)؛
  • انتقال تدریجی اعضا بر اساس معیار تفاوت میانگین فاصله؛
  • ثبت تاریخچه تقسیم‌ها و تحلیل ساختار سلسله‌مراتبی حاصل.

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

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

.

۲. مسیرهای توسعه و ایده‌های نوین مرتبط با الگوریتم DIANA

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

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

.

۲.۱ خوشه‌بندی سلسله‌مراتبی مقیاس‌پذیر

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

برای افزایش مقیاس‌پذیری، روش‌های زیر می‌توانند در نسخه‌های توسعه‌یافته DIANA مورد استفاده قرار گیرند:

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

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

.

۲.۲ ترکیب DIANA با کاهش بعد و یادگیری نمایش

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

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

روش‌های قابل استفاده شامل:

  • PCA برای کاهش بعد خطی و حذف همبستگی‌های اضافی؛
  • Autoencoder برای یادگیری نمایش‌های غیرخطی؛
  • Embedding Models برای تبدیل داده‌های متنی، تصویری یا چندرسانه‌ای به بردارهای معنایی.

در این میان، روش‌هایی مانند t-SNE بیشتر برای مصورسازی داده‌ها طراحی شده‌اند و استفاده مستقیم از خروجی آن‌ها برای خوشه‌بندی باید با احتیاط انجام شود، زیرا الزاماً فاصله‌های واقعی فضای اصلی را حفظ نمی‌کنند.

.

۲.۳ ترکیب DIANA با یادگیری نمایش عمیق

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

برای مثال:

  • در داده‌های تصویری می‌توان از ویژگی‌های استخراج‌شده توسط شبکه‌های عمیق استفاده کرد.
  • در داده‌های متنی می‌توان از embeddingهای مدل‌هایی مانند BERT یا Sentence-BERT استفاده کرد.
  • در داده‌های چندرسانه‌ای می‌توان نمایش مشترک چند نوع داده را ایجاد کرد.

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

.

۲.۴ افزایش تفسیرپذیری خوشه‌بندی

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

مسیرهای توسعه در این زمینه شامل:

  • استخراج ویژگی‌های مهم هر شاخه از درخت خوشه‌بندی؛
  • ایجاد قوانین توصیفی برای توضیح تفاوت میان خوشه‌ها؛
  • استفاده از مدل‌های جانشین برای پیش‌بینی برچسب خوشه‌ها؛
  • استفاده از روش‌های توضیح‌پذیری مانند SHAP روی مدل‌های جانشین.

لازم است توجه شود که روش‌هایی مانند SHAP به‌صورت مستقیم خروجی یک الگوریتم بدون ناظر مانند DIANA را توضیح نمی‌دهند؛ بلکه معمولاً پس از آموزش یک مدل تقریبی برای پیش‌بینی عضویت خوشه‌ها استفاده می‌شوند.

.

۲.۵ استفاده از DIANA در داده‌های گرافی

بسیاری از داده‌های جدید دارای ساختار شبکه‌ای هستند؛ مانند:

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

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

روش‌های قابل استفاده شامل:

  • Node2Vec؛
  • GraphSAGE؛
  • embeddingهای طیفی مبتنی بر ماتریس لاپلاسی گراف.

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

.

۲.۶ افزایش مقاومت در برابر نویز و داده‌های پرت

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

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

  • شناسایی نقاط پرت پیش از اجرای الگوریتم؛
  • استفاده از معیارهای فاصله مقاوم‌تر؛
  • استفاده از معیارهای مبتنی بر میانه به جای میانگین فاصله؛
  • ترکیب DIANA با روش‌های تشخیص ناهنجاری مانند Isolation Forest یا Local Outlier Factor.

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

.

۲.۷ جمع‌بندی مسیرهای توسعه

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

جهت‌گیری‌های جدید بیشتر بر سه هدف تمرکز دارند:

۱. افزایش مقیاس‌پذیری برای داده‌های بزرگ؛
۲. استفاده از نمایش‌های غنی‌تر داده به جای ویژگی‌های خام؛
۳. افزایش تفسیرپذیری و مقاومت در برابر نویز.

بنابراین، آینده DIANA بیشتر در قالب نسخه‌های ترکیبی با روش‌های جدید یادگیری ماشین قابل تصور است، نه تغییر بنیادین الگوریتم اصلی.

.

۳. معرفی کتابخانه‌ها و ابزارهای مورد استفاده در پیاده‌سازی DIANA

برای پیاده‌سازی الگوریتم DIANA در Python، معمولاً از ترکیبی از کتابخانه‌های محاسبات عددی، پردازش داده، ارزیابی خوشه‌بندی و مصورسازی استفاده می‌شود. از آنجا که در کتابخانه‌های رایج Python مانند scikit-learn پیاده‌سازی مستقیم و استانداردی از DIANA وجود ندارد، این الگوریتم معمولاً باید از پایه پیاده‌سازی شده یا با ابزارهای کمکی تکمیل شود.

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

.

۳.۱ کتابخانه NumPy

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

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

نمونه فراخوانی:

import numpy as np

۳.۲ کتابخانه scikit-learn

کتابخانه scikit-learn یکی از مهم‌ترین ابزارهای یادگیری ماشین در Python است. این کتابخانه الگوریتم DIANA را به‌صورت مستقیم ارائه نمی‌کند، اما برای آماده‌سازی داده‌ها، ارزیابی خروجی و مقایسه الگوریتم بسیار کاربردی است.

کاربردهای اصلی آن در این پروژه:

  • استانداردسازی داده‌ها؛
  • محاسبه معیارهای ارزیابی خوشه‌بندی؛
  • اجرای الگوریتم‌های مقایسه‌ای مانند K-means، DBSCAN و خوشه‌بندی تجمیعی؛
  • تولید داده‌های آزمایشی.

نمونه:

from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score

همچنین می‌توان برای مقایسه عملکرد DIANA از الگوریتم‌هایی مانند:

from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering

استفاده کرد.

۳.۳ کتابخانه SciPy

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

کاربردهای مرتبط با DIANA:

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

نکته مهم این است که توابع سلسله‌مراتبی SciPy عمدتاً برای روش‌های تجمیعی (Agglomerative) طراحی شده‌اند و پیاده‌سازی مستقیم DIANA را ارائه نمی‌کنند. بنابراین در این پروژه از SciPy بیشتر به‌عنوان ابزار کمکی استفاده می‌شود.

نمونه:

from scipy.spatial.distance import pdist, squareform

۳.۴ کتابخانه pandas

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

کاربردهای آن شامل:

  • خواندن داده‌ها از فایل‌های CSV و Excel؛
  • بررسی داده‌های گمشده؛
  • انتخاب ویژگی‌ها؛
  • آماده‌سازی داده برای ورود به الگوریتم.

نمونه:

import pandas as pd

۳.۵ کتابخانه Matplotlib

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

کاربردهای آن در این پروژه:

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

نمونه:

import matplotlib.pyplot as plt

۳.۶ کتابخانه Seaborn

کتابخانه Seaborn بر پایه Matplotlib ساخته شده و برای ایجاد نمودارهای آماری استفاده می‌شود.

در تحلیل خوشه‌بندی می‌توان از آن برای:

  • بررسی روابط بین ویژگی‌ها؛
  • تحلیل توزیع داده‌ها؛
  • نمایش الگوهای آماری قبل و بعد از خوشه‌بندی

استفاده کرد.

نمونه:

import seaborn as sns

استفاده از Seaborn برای اجرای خود الگوریتم ضروری نیست و بیشتر نقش تحلیلی و تصویری دارد.

۳.۷ کتابخانه cluster در زبان R

اگرچه تمرکز این فصل روی Python است، اما لازم است اشاره شود که یکی از پیاده‌سازی‌های شناخته‌شده DIANA در بسته cluster زبان R وجود دارد.

این بسته تابع diana() را برای اجرای الگوریتم DIANA ارائه می‌دهد و می‌تواند به‌عنوان مرجع مقایسه‌ای برای بررسی صحت پیاده‌سازی Python استفاده شود.

نمونه:

library(cluster)
result <- diana(data)
plot(result)

مزیت استفاده از این پیاده‌سازی:

  • استاندارد و شناخته‌شده بودن؛
  • امکان مقایسه نتایج؛
  • تولید خروجی سلسله‌مراتبی.

۳.۸ کتابخانه HDBSCAN

الگوریتم HDBSCAN یک روش خوشه‌بندی سلسله‌مراتبی مبتنی بر چگالی است و ارتباط مستقیمی با DIANA ندارد، اما می‌تواند به‌عنوان یک روش مقایسه‌ای استفاده شود.

تفاوت اصلی:

  • DIANA بر اساس فاصله و تقسیم سلسله‌مراتبی عمل می‌کند.
  • HDBSCAN بر اساس ساختار چگالی داده‌ها خوشه‌ها را استخراج می‌کند.

این مقایسه می‌تواند نشان دهد که انتخاب روش خوشه‌بندی چگونه به ماهیت داده وابسته است.

نمونه:

import hdbscan

جمع‌بندی بخش کتابخانه‌ها

در پیاده‌سازی عملی DIANA، سه گروه ابزار مورد استفاده قرار می‌گیرند:

  • ابزارهای محاسباتی: NumPy، SciPy
  • ابزارهای آماده‌سازی و ارزیابی: pandas، scikit-learn
  • ابزارهای تحلیل و نمایش: Matplotlib، Seaborn

همچنین بسته cluster در R می‌تواند به‌عنوان یک مرجع استاندارد برای مقایسه نتایج پیاده‌سازی Python استفاده شود.

.

4. مطالعات موردی

مطالعه موردی ۱: بخش‌بندی مشتریان بانک با استفاده از الگوریتم DIANA

معرفی مسئله

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

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

ویژگیتوضیح
Ageسن مشتری
Incomeدرآمد سالانه
Transactionsتعداد تراکنش ماهانه
Balanceمیانگین موجودی حساب
Digital_Usageمیزان استفاده از خدمات دیجیتال
Loanوضعیت استفاده از تسهیلات

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

هدف آزمایش

هدف:

تقسیم مشتریان به سه گروه اصلی:

  • مشتریان دیجیتال و فعال؛
  • مشتریان سنتی با تعامل کمتر؛
  • مشتریان با رفتار مالی متفاوت.

کد کامل Python

کد زیر مستقل است و مستقیم قابل اجراست.

نیازمندی‌ها:


pip install numpy pandas matplotlib scikit-learn
# ==========================================================
# بخش بندی مشتریان بانک با الگوریتم DIANA
# پیاده سازی آموزشی کامل
# ==========================================================

# کتابخانه های مورد نیاز

import numpy as np
import pandas as pd

import matplotlib.pyplot as plt

from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score
from sklearn.decomposition import PCA

# ==========================================================
# پیاده سازی الگوریتم DIANA
# ==========================================================

class DianaClustering:

    def __init__(self, n_clusters=3):

        # تعداد خوشه های نهایی مورد نظر
        self.n_clusters = n_clusters

        # ذخیره خوشه های نهایی
        self.clusters_ = None

    def average_distance(self, D, point, group):

        """
        محاسبه میانگین فاصله یک نقطه
        از اعضای یک گروه
        """

        distances = []

        for other in group:

            if point != other:
                distances.append(
                    D[point, other]
                )

        if len(distances) == 0:
            return 0

        return np.mean(distances)


    def diameter(self, D, cluster):

        """
        محاسبه قطر خوشه
        """

        maximum = 0

        for i in cluster:

            for j in cluster:

                if D[i,j] > maximum:
                    maximum = D[i,j]

        return maximum


    def split_cluster(self, D, cluster):

        """
        تقسیم یک خوشه بر اساس Splinter Group
        """

        # پیدا کردن دورترین عضو

        scores = []

        for point in cluster:

            score = self.average_distance(
                D,
                point,
                cluster
            )

            scores.append(score)


        seed = cluster[
            np.argmax(scores)
        ]

        # گروه جداشونده اولیه

        splinter = [seed]

        # گروه باقی مانده

        remaining = [
            x for x in cluster
            if x != seed
        ]

        # انتقال اعضا

        while True:

            best_point = None
            best_delta = 0

            for point in remaining:

                d_remaining = self.average_distance(
                    D,
                    point,
                    remaining
                )

                d_splinter = self.average_distance(
                    D,
                    point,
                    splinter
                )

                delta = (
                    d_remaining
                    -
                    d_splinter
                )

                if delta > best_delta:

                    best_delta = delta
                    best_point = point


            if best_point is None:
                break

            splinter.append(
                best_point
            )

            remaining.remove(
                best_point
            )

        return remaining, splinter


    def fit(self,X):

        n = len(X)

        # محاسبه ماتریس فاصله

        from sklearn.metrics import pairwise_distances

        D = pairwise_distances(
            X
        )

        clusters = [
            list(range(n))
        ]


        # ادامه تقسیم تا رسیدن به تعداد خوشه مورد نظر

        while len(clusters) < self.n_clusters:

            # انتخاب ناهمگن ترین خوشه

            index = np.argmax(

                [
                    self.diameter(D,c)
                    for c in clusters
                ]

            )

            cluster = clusters.pop(index)


            left,right = self.split_cluster(
                D,
                cluster
            )

            clusters.append(left)
            clusters.append(right)
        self.clusters_ = clusters

        # تولید برچسب

        labels = np.zeros(n,dtype=int)

        for i,c in enumerate(clusters):

            for item in c:

                labels[item]=i

        return labels

# ==========================================================
# ایجاد داده نمونه مشتریان بانک
# ==========================================================

np.random.seed(42)

customers = pd.DataFrame({

    "Age":
    np.random.randint(
        20,
        65,
        120
    ),

    "Income":
    np.random.randint(
        20,
        150,
        120
    ),

    "Transactions":
    np.random.randint(
        5,
        100,
        120
    ),
    "Balance":
    np.random.randint(
        10,
        500,
        120
    ),
    "Digital_Usage":
    np.random.randint(
        0,
        10,
        120
    ),

    "Loan":
    np.random.randint(
        0,
        2,
        120
    )

})

print(customers.head())
# ==========================================================
# استانداردسازی داده ها
# ==========================================================

scaler = StandardScaler()

X_scaled = scaler.fit_transform(
    customers
)
# ==========================================================
# اجرای DIANA
# ==========================================================

model = DianaClustering(
    n_clusters=3
)
labels = model.fit(
    X_scaled
)
customers["Cluster"] = labels
print(
    "\nتعداد مشتریان هر خوشه:"
)

print(
    customers["Cluster"].value_counts()
)

# ==========================================================
# ارزیابی خوشه بندی
# ==========================================================

score = silhouette_score(
    X_scaled,
    labels
)
print(
    "\nSilhouette Score:",
    round(score,4)
)
# ==========================================================
# نمایش دوبعدی با PCA
# ==========================================================
pca = PCA(
    n_components=2
)
X_2D = pca.fit_transform(
    X_scaled
)

plt.figure(
    figsize=(8,6)
)
plt.scatter(

    X_2D[:,0],

    X_2D[:,1],

    c=labels,

    s=70

)
plt.title(
    "Bank Customer Segmentation using DIANA"
)
plt.xlabel(
    "Principal Component 1"
)
plt.ylabel(
    "Principal Component 2"
)
plt.grid(
    alpha=0.3
)
plt.show()

خروجی:

تحلیل خروجی

نتیجه اجرای الگوریتم DIANA نشان می‌دهد که ۱۲۰ مشتری بانک بر اساس ویژگی‌های رفتاری و مالی به سه خوشه مجزا تقسیم شده‌اند.

۱. توزیع اعضای خوشه‌ها

خروجی الگوریتم:

  • Cluster 0: تعداد ۴۷ مشتری
  • Cluster 1: تعداد ۴۰ مشتری
  • Cluster 2: تعداد ۳۳ مشتری

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


۲. تحلیل شاخص Silhouette

مقدار به‌دست‌آمده:

Silhouette Score = 0.126

این مقدار نشان می‌دهد که جداسازی خوشه‌ها ضعیف تا متوسط است.

تفسیر:

  • مقدار نزدیک به ۱ نشان‌دهنده خوشه‌های کاملاً جدا از هم است.
  • مقدار نزدیک به صفر نشان می‌دهد که مرز بین خوشه‌ها واضح نیست.
  • مقدار منفی نشان‌دهنده تخصیص نامناسب نمونه‌ها است.

بنابراین مقدار ۰٫۱۲۶ بیان می‌کند که اگرچه DIANA توانسته است سه گروه ایجاد کند، اما شباهت زیادی بین برخی مشتریان خوشه‌های مختلف وجود دارد.

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


۳. تحلیل نمودار PCA

در نمودار دوبعدی حاصل از کاهش بعد PCA:

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

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


۴. تفسیر کسب‌وکاری

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

  • Cluster 0: مشتریان عمومی یا سنتی
  • تعامل متوسط با بانک؛
  • استفاده محدودتر از خدمات دیجیتال؛
  • رفتار مالی معمولی.
  • Cluster 1: مشتریان دیجیتال فعال
  • استفاده بیشتر از خدمات آنلاین؛
  • تعداد تراکنش بالاتر؛
  • مناسب برای پیشنهاد خدمات دیجیتال و بانکداری هوشمند.
  • Cluster 2: مشتریان با ارزش مالی بالاتر
  • درآمد و موجودی بیشتر؛
  • مناسب برای خدمات ویژه مانند تسهیلات اختصاصی یا مدیریت ثروت.

(برای نام‌گذاری قطعی خوشه‌ها باید میانگین ویژگی‌های هر خوشه بررسی شود.)

.

مطالعه موردی ۲: تحلیل رخدادهای HSE در صنعت با استفاده از الگوریتم DIANA

معرفی مسئله

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

در این مطالعه، هدف استفاده از DIANA برای کشف ساختار سلسله‌مراتبی رخدادهای HSE است.

فرض می‌کنیم اطلاعات هر حادثه شامل ویژگی‌های زیر باشد:

ویژگیتوضیح
Incident_Typeنوع حادثه
Severityشدت پیامد
Human_Factorمیزان نقش عامل انسانی
Equipment_Failureمیزان نقش خرابی تجهیز
Training_Levelسطح آموزش کارکنان
Environmental_Riskشرایط محیطی
Shiftشیفت کاری

هدف الگوریتم:

تقسیم رخدادها به گروه‌های اصلی مانند:

  • حوادث ناشی از عوامل انسانی؛
  • حوادث ناشی از خرابی تجهیزات؛
  • حوادث محیطی و فرایندی.

روش اجرا

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

کد کامل Python

pip install numpy pandas matplotlib scikit-learn

# ==========================================================
# تحلیل رخدادهای HSE صنعتی با الگوریتم DIANA
# پیاده سازی کامل آموزشی
# ==========================================================

import numpy as np
import pandas as pd

import matplotlib.pyplot as plt

from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score
from sklearn.decomposition import PCA


# ==========================================================
# پیاده سازی الگوریتم DIANA
# ==========================================================

class DianaClustering:

    def __init__(self, n_clusters=3):

        # تعداد خوشه های نهایی
        self.n_clusters = n_clusters

        self.clusters_ = None


    def average_distance(self, D, point, group):

        """
        میانگین فاصله یک رخداد
        نسبت به یک گروه از رخدادها
        """

        distances = []

        for other in group:

            if point != other:

                distances.append(
                    D[point, other]
                )

        if len(distances)==0:
            return 0

        return np.mean(distances)


    def cluster_diameter(self,D,cluster):

        """
        محاسبه قطر خوشه برای پیدا کردن
        ناهمگن ترین گروه
        """

        maximum=0

        for i in cluster:

            for j in cluster:

                if D[i,j] > maximum:

                    maximum=D[i,j]

        return maximum


    def split_cluster(self,D,cluster):

        """
        ایجاد گروه جداشونده Splinter Group
        """

        scores=[]

        # پیدا کردن رخداد با بیشترین فاصله متوسط

        for point in cluster:

            score=self.average_distance(
                D,
                point,
                cluster
            )

            scores.append(score)


        seed=cluster[
            np.argmax(scores)
        ]


        # گروه جداشونده اولیه

        splinter=[seed]

        # گروه باقی مانده

        remaining=[
            x for x in cluster
            if x!=seed
        ]


        # بررسی انتقال اعضا

        while True:

            best_point=None

            best_delta=0


            for point in remaining:

                d_remaining=self.average_distance(
                    D,
                    point,
                    remaining
                )

                d_splinter=self.average_distance(
                    D,
                    point,
                    splinter
                )

                delta=(
                    d_remaining
                    -
                    d_splinter
                )


                if delta>best_delta:

                    best_delta=delta

                    best_point=point


            if best_point is None:

                break


            splinter.append(
                best_point
            )

            remaining.remove(
                best_point
            )


        return remaining,splinter



    def fit(self,X):

        from sklearn.metrics import pairwise_distances

        # محاسبه ماتریس فاصله

        D=pairwise_distances(
            X
        )


        n=len(X)


        # شروع با یک خوشه شامل همه رخدادها

        clusters=[
            list(range(n))
        ]


        # تقسیم تا رسیدن به تعداد خوشه مورد نظر

        while len(clusters)<self.n_clusters:


            index=np.argmax(

                [
                    self.cluster_diameter(D,c)
                    for c in clusters
                ]

            )

            selected=clusters.pop(index)


            group1,group2=self.split_cluster(
                D,
                selected
            )


            clusters.append(group1)

            clusters.append(group2)


        self.clusters_=clusters


        # تبدیل خوشه ها به برچسب

        labels=np.zeros(
            n,
            dtype=int
        )

        for i,c in enumerate(clusters):

            for item in c:

                labels[item]=i


        return labels


# ==========================================================
# ایجاد داده نمونه رخدادهای HSE
# ==========================================================

np.random.seed(10)


hse_data=pd.DataFrame({

    # شدت حادثه
    "Severity":
    np.random.randint(
        1,
        10,
        100
    ),

    # خطای انسانی
    "Human_Factor":
    np.random.randint(
        0,
        10,
        100
    ),

    # خرابی تجهیز
    "Equipment_Failure":
    np.random.randint(
        0,
        10,
        100
    ),

    # سطح آموزش
    "Training_Level":
    np.random.randint(
        1,
        5,
        100
    ),

    # ریسک محیطی
    "Environmental_Risk":
    np.random.randint(
        0,
        10,
        100
    ),

    # شیفت کاری
    "Shift":
    np.random.randint(
        1,
        4,
        100
    )

})


print(
    hse_data.head()
)


# ==========================================================
# استانداردسازی ویژگی ها
# ==========================================================

scaler=StandardScaler()

X=scaler.fit_transform(
    hse_data
)


# ==========================================================
# اجرای DIANA
# ==========================================================

model=DianaClustering(
    n_clusters=3
)

labels=model.fit(
    X
)


hse_data["Cluster"]=labels


print(
    "\nتعداد رخدادها در هر گروه:"
)

print(
    hse_data["Cluster"].value_counts()
)


# ==========================================================
# ارزیابی کیفیت خوشه بندی
# ==========================================================

score=silhouette_score(
    X,
    labels
)


print(
    "\nSilhouette Score:",
    round(score,3)
)


# ==========================================================
# نمایش دوبعدی PCA
# ==========================================================

pca=PCA(
    n_components=2
)

X_pca=pca.fit_transform(
    X
)


plt.figure(
    figsize=(8,6)
)


plt.scatter(

    X_pca[:,0],

    X_pca[:,1],

    c=labels,

    s=70

)


plt.title(
    "Industrial HSE Incident Clustering using DIANA"
)

plt.xlabel(
    "Principal Component 1"
)

plt.ylabel(
    "Principal Component 2"
)

plt.grid(
    alpha=0.3
)

plt.show()

خروجی:

5. دو مثال عددی

مثال پیشرفته 1: داده دوبعدی با سه ساختار کلان

فرض کنید مجموعه‌داده زیر شامل 9 نقطه دوبعدی باشد:

نقطهمختصات
A(1, 1)
B(1, 2)
C(2, 1)
D(8, 8)
E(8, 9)
F(9, 8)
G(20, 2)
H(21, 2)
I(20, 3)

از نظر بصری سه گروه طبیعی وجود دارد:

C1={A,B,C}

C2={D,E,F}

C3={G,H,I}

  • گام اول: تشکیل خوشه اولیه

در ابتدا همه نقاط در یک خوشه قرار دارند:

C={A,B,C,D,E,F,G,H,I}

  • گام دوم: محاسبه میانگین فاصله هر نقطه

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

فرض کنیم نقطه H به عنوان آغازگر انتخاب شود:

As={H}

و گروه باقی‌مانده:

Bs={A,B,C,D,E,F,G,I}

  • گام سوم: انتقال اعضای مشابه به گروه جداشونده

در ادامه، برای هر نقطه در Bs​ مقدار زیر بررسی می‌شود:

Δ(x)=dˉ(x,Bs)−dˉ(x,As)

اگر مقدار  مثبت باشد، نقطه x به گروه جداشونده نزدیک‌تر از گروه باقی‌مانده تلقی شده و منتقل می‌شود.

در این مثال، نقاط G و I به دلیل نزدیکی به H به گروه جداشونده اضافه می‌شوند:

As={G,H,I}

نتیجه تقسیم اول

تقسیم اول به صورت زیر خواهد بود:

Ca(1)={G,H,I}

Cb(1)={A,B,C,D,E,F}

تقسیم دوم

اکنون ناهمگن‌ترین خوشه موجود احتمالاً خوشه زیر است:

{A,B,C,D,E,F}

DIANA همین فرایند را روی آن اجرا می‌کند و آن را به دو گروه زیر تقسیم می‌کند:

Ca(2)={A,B,C}

Cb(2)={D,E,F}

خروجی نهایی برای سه خوشه

اگر دندروگرام در سطح سه خوشه برش داده شود، خروجی نهایی چنین است:

{A,B,C},{D,E,F},{G,H,I}


مثال پیشرفته 2: داده با یک نقطه پرت

فرض کنید داده‌های زیر را داشته باشیم:

نقطهمختصات
A(1, 1)
B(1, 2)
C(2, 1)
D(2, 2)
E(10, 10)
F(10, 11)
G(11, 10)
H(50, 50)

در این داده، دو خوشه طبیعی و یک نقطه پرت وجود دارد:

C1={A,B,C,D}

C2={E,F,G}

O={H}

  • گام اول: خوشه اولیه

C={A,B,C,D,E,F,G,H}

  • گام دوم: انتخاب عضو آغازگر

نقطه H فاصله بسیار زیادی از همه نقاط دیگر دارد؛ بنابراین میانگین فاصله آن از سایر اعضا بیشترین مقدار خواهد بود. در نتیجه، DIANA احتمالاً آن را به عنوان آغازگر گروه جداشونده انتخاب می‌کند:

As={H}

  • گام سوم: بررسی انتقال سایر نقاط

برای نقاط A تا G، میانگین فاصله آن‌ها تا گروه باقی‌مانده کمتر از فاصله آن‌ها تا نقطه H است. بنابراین هیچ‌یک از آن‌ها به گروه جداشونده منتقل نمی‌شوند.

نتیجه تقسیم اول

Ca(1)={H}

Cb(1)={A,B,C,D,E,F,G}

تفسیر

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

ادامه تقسیم

پس از جدا شدن H، خوشه باقی‌مانده به احتمال زیاد در مرحله بعد به دو خوشه زیر تقسیم می‌شود:

{A,B,C,D},{E,F,G}

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

.

۶. پیاده‌سازی آموزشی الگوریتم DIANA در Python

۶.۱ مقدمه پیاده‌سازی

از آنجا که الگوریتم DIANA در کتابخانه‌های استاندارد Python مانند scikit-learn به‌صورت مستقیم پیاده‌سازی نشده است، در این بخش یک نسخه آموزشی از الگوریتم از پایه توسعه داده می‌شود.

هدف این پیاده‌سازی، نمایش مراحل اصلی DIANA شامل:

  • تشکیل ماتریس فاصله؛
  • انتخاب ناهمگن‌ترین خوشه برای تقسیم؛
  • انتخاب عضو آغازگر گروه جداشونده (Splinter Group)؛
  • انتقال اعضا بر اساس معیار تفاوت میانگین فاصله؛
  • ذخیره تاریخچه تقسیم‌ها؛
  • تولید برچسب نهایی خوشه‌ها

است.

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

۶.۲ کتابخانه‌های مورد نیاز

import numpy as np
import matplotlib.pyplot as plt

from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import (
    silhouette_score,
    adjusted_rand_score,
    pairwise_distances
)

from sklearn.decomposition import PCA

۶.۳ تعریف کلاس DIANA

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

ویژگی‌های اصلی:

  • تعیین تعداد خوشه‌های نهایی؛
  • انتخاب معیار فاصله؛
  • ذخیره خوشه‌های نهایی؛
  • ذخیره تاریخچه تقسیم‌ها.
class DianaClustering:
    """
    پیاده سازی آموزشی الگوریتم DIANA
    """

    def __init__(
        self,
        n_clusters=3,
        metric="euclidean"
    ):

        # تعداد خوشه های مورد انتظار
        self.n_clusters = n_clusters

        # معیار فاصله
        self.metric = metric

        # ذخیره خروجی نهایی
        self.labels_ = None

        self.clusters_ = None

        # ذخیره مسیر تقسیم ها
        self.history_ = []

 

۶.۴ محاسبه میانگین فاصله یک نمونه از یک گروه

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

def _average_distance_to_group(
        self,
        distance_matrix,
        point,
        group
):

    distances = []

    for other in group:

        if other != point:

            distances.append(
                distance_matrix[
                    point,
                    other
                ]
            )

    if len(distances)==0:

        return 0

    return np.mean(distances)

۶.۵ محاسبه قطر خوشه

برای انتخاب ناهمگن‌ترین خوشه، قطر هر خوشه محاسبه می‌شود.

def _cluster_diameter(
        self,
        distance_matrix,
        cluster
):

    maximum_distance = 0

    for i in cluster:

        for j in cluster:

            distance = distance_matrix[i,j]

            if distance > maximum_distance:

                maximum_distance = distance

    return maximum_distance

۶.۶ انتخاب خوشه برای تقسیم

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

def _select_cluster_to_split(
        self,
        distance_matrix,
        clusters
):

    diameters = []

    for cluster in clusters:

        diameters.append(

            self._cluster_diameter(
                distance_matrix,
                cluster
            )

        )

    return np.argmax(diameters)

۶.۷ عملیات تقسیم خوشه

در این مرحله:

۱. عضو آغازگر گروه جداشونده انتخاب می‌شود.
۲. اعضای مشابه به آن منتقل می‌شوند.
۳. دو گروه جدید ایجاد می‌شوند.

def _split_cluster(
        self,
        distance_matrix,
        cluster
):

    cluster=list(cluster)

    # محاسبه فاصله متوسط هر عضو

    avg_distances=[]

    for point in cluster:

        value=self._average_distance_to_group(
            distance_matrix,
            point,
            cluster
        )

        avg_distances.append(value)


    # انتخاب عضو آغازگر

    seed=cluster[
        np.argmax(avg_distances)
    ]


    splinter_group=[seed]

    remaining_group=[

        x for x in cluster

        if x != seed

    ]


    # انتقال اعضای مناسب

    changed=True

    while changed:

        changed=False

        best_point=None

        best_delta=0


        for point in remaining_group:

            d_remaining = self._average_distance_to_group(
                distance_matrix,
                point,
                remaining_group
            )

            d_splinter = self._average_distance_to_group(
                distance_matrix,
                point,
                splinter_group
            )

            delta = (
                d_remaining
                -
                d_splinter
            )

            if delta > best_delta:

                best_delta=delta

                best_point=point


        if best_point is not None:

            splinter_group.append(
                best_point
            )

            remaining_group.remove(
                best_point
            )

            changed=True

    return remaining_group, splinter_group

۶.۸ اجرای الگوریتم روی داده

تابع fit فرآیند کامل DIANA را اجرا می‌کند.

def fit(self, X):

    X=np.asarray(X)

    # بررسی داده ورودی

    if X.ndim != 2:

        raise ValueError(
            "Input data must be 2D"
        )

    if not np.isfinite(X).all():

        raise ValueError(
            "Data contains invalid values"
        )


    # ساخت ماتریس فاصله

    distance_matrix = pairwise_distances(
        X,
        metric=self.metric
    )

    n_samples=len(X)

    clusters=[

        list(range(n_samples))

    ]

    self.history_=[]


    while len(clusters)<self.n_clusters:


        index=self._select_cluster_to_split(
            distance_matrix,
            clusters
        )

        selected=clusters.pop(index)


        group1,group2=self._split_cluster(
            distance_matrix,
            selected
        )


        clusters.append(group1)

        clusters.append(group2)


        self.history_.append({

            "original_cluster":selected,

            "remaining_group":group1,

            "splinter_group":group2

        })


    labels=np.zeros(
        n_samples,
        dtype=int
    )


    for cluster_id,cluster in enumerate(clusters):

        for sample in cluster:

            labels[sample]=cluster_id


    self.labels_=labels

    self.clusters_=clusters


    return self

6.9.کد پایتون کامل


# ==========================================================
# اجرای کامل الگوریتم DIANA در Python
# مثال آموزشی با داده مصنوعی سه خوشه‌ای
# ==========================================================

# کتابخانه‌های مورد نیاز

import numpy as np
import matplotlib.pyplot as plt

from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler

from sklearn.metrics import (
    silhouette_score,
    adjusted_rand_score,
    pairwise_distances
)

from sklearn.decomposition import PCA


# ==========================================================
# تعریف کلاس DIANA
# ==========================================================

class DianaClustering:

    def __init__(
        self,
        n_clusters=3,
        metric="euclidean"
    ):

        # تعداد خوشه‌های نهایی
        self.n_clusters = n_clusters

        # معیار فاصله
        self.metric = metric

        # ذخیره برچسب نهایی نمونه‌ها
        self.labels_ = None

        # ذخیره خوشه‌های نهایی
        self.clusters_ = None

        # ذخیره تاریخچه تقسیم‌ها
        self.history_ = []


    # ------------------------------------------------------
    # محاسبه میانگین فاصله یک نقطه از یک گروه
    # ------------------------------------------------------

    def average_distance(
        self,
        distance_matrix,
        point,
        group
    ):

        distances = []

        for other in group:

            if other != point:

                distances.append(
                    distance_matrix[
                        point,
                        other
                    ]
                )

        if len(distances) == 0:

            return 0

        return np.mean(distances)


    # ------------------------------------------------------
    # محاسبه قطر خوشه
    # ------------------------------------------------------

    def cluster_diameter(
        self,
        distance_matrix,
        cluster
    ):

        maximum = 0

        for i in cluster:

            for j in cluster:

                if distance_matrix[i,j] > maximum:

                    maximum = distance_matrix[i,j]

        return maximum


    # ------------------------------------------------------
    # انتخاب ناهمگن‌ترین خوشه برای تقسیم
    # ------------------------------------------------------

    def select_cluster_to_split(
        self,
        distance_matrix,
        clusters
    ):

        diameters = []

        for cluster in clusters:

            diameters.append(

                self.cluster_diameter(
                    distance_matrix,
                    cluster
                )

            )

        return np.argmax(diameters)


    # ------------------------------------------------------
    # تقسیم یک خوشه به دو گروه
    # ------------------------------------------------------

    def split_cluster(
        self,
        distance_matrix,
        cluster
    ):

        cluster = list(cluster)

        # محاسبه میانگین فاصله هر عضو

        avg_distances=[]

        for point in cluster:

            avg_distances.append(

                self.average_distance(
                    distance_matrix,
                    point,
                    cluster
                )

            )


        # انتخاب عضو آغازگر Splinter Group

        seed = cluster[
            np.argmax(avg_distances)
        ]


        # ایجاد گروه جداشونده

        splinter_group=[seed]

        # ایجاد گروه باقی‌مانده

        remaining_group=[

            x for x in cluster

            if x != seed

        ]


        # انتقال اعضای مناسب به گروه جداشونده

        changed=True

        while changed:

            changed=False

            best_point=None

            best_delta=0


            for point in remaining_group:

                distance_remaining = self.average_distance(

                    distance_matrix,

                    point,

                    remaining_group

                )

                distance_splinter = self.average_distance(

                    distance_matrix,

                    point,

                    splinter_group

                )

                delta = (

                    distance_remaining

                    -

                    distance_splinter

                )


                if delta > best_delta:

                    best_delta = delta

                    best_point = point


            if best_point is not None:

                splinter_group.append(
                    best_point
                )

                remaining_group.remove(
                    best_point
                )

                changed=True


        return remaining_group, splinter_group


    # ------------------------------------------------------
    # اجرای الگوریتم DIANA
    # ------------------------------------------------------

    def fit(self, X):

        X=np.asarray(X)

        # کنترل ورودی

        if X.ndim != 2:

            raise ValueError(
                "Input data must be two-dimensional"
            )


        # ساخت ماتریس فاصله

        distance_matrix = pairwise_distances(

            X,

            metric=self.metric

        )


        n_samples=len(X)


        # شروع با یک خوشه شامل همه داده‌ها

        clusters=[

            list(range(n_samples))

        ]
        self.history_=[]
        # تقسیم تا رسیدن به تعداد خوشه مورد نظر

        while len(clusters)<self.n_clusters:

            index=self.select_cluster_to_split(

                distance_matrix,

                clusters

            )

            selected_cluster = clusters.pop(index)
            remaining, splinter = self.split_cluster(

                distance_matrix,

                selected_cluster

            )
            clusters.append(
                remaining
            )

            clusters.append(
                splinter
            )


            # ذخیره تاریخچه تقسیم

            self.history_.append({

                "original_cluster":

                    selected_cluster,

                "remaining_group":

                    remaining,

                "splinter_group":

                    splinter

            })
        # ایجاد برچسب خوشه‌ها

        labels=np.zeros(

            n_samples,

            dtype=int

        )
        for cluster_id,cluster in enumerate(clusters):

            for sample in cluster:

                labels[sample]=cluster_id
        self.labels_=labels

        self.clusters_=clusters
        return self
    def fit_predict(self,X):

        self.fit(X)

        return self.labels_

# ==========================================================
# تولید داده آزمایشی
# ==========================================================

X, y_true = make_blobs(

    n_samples=120,

    centers=3,

    cluster_std=[

        0.7,

        0.9,

        0.8

    ],

    random_state=42

)

# استانداردسازی داده‌ها

scaler = StandardScaler()
X_scaled = scaler.fit_transform(
    X
)

# ==========================================================
# اجرای DIANA
# ==========================================================

model = DianaClustering(

    n_clusters=3,

    metric="euclidean"

)

labels = model.fit_predict(

    X_scaled

)

# ==========================================================
# نمایش نتایج عددی
# ==========================================================

print("\nخوشه‌های نهایی:")

for i,cluster in enumerate(model.clusters_):

    print(
        f"Cluster {i}: {len(cluster)} samples"
    )

print("\nتاریخچه تقسیم‌ها:")

for i,item in enumerate(

    model.history_,

    start=1

):

    print(
        f"\nمرحله {i}"
    )

    print(
        "Splinter Group:",
        item["splinter_group"]
    )

# ==========================================================
# ارزیابی عملکرد
# ==========================================================

silhouette = silhouette_score(

    X_scaled,

    labels

)
ari = adjusted_rand_score(

    y_true,

    labels

)

print(
    "\nSilhouette Score:",
    round(silhouette,4)
)


print(
    "Adjusted Rand Index:",
    round(ari,4)
)

# ==========================================================
# نمایش دوبعدی نتیجه
# ==========================================================

pca = PCA(
    n_components=2
)

X_pca = pca.fit_transform(

    X_scaled

)

plt.figure(

    figsize=(8,6)

)

plt.scatter(

    X_pca[:,0],

    X_pca[:,1],

    c=labels,

    s=70

)


plt.title(

    "DIANA Clustering Result"

)


plt.xlabel(

    "Principal Component 1"

)

plt.ylabel(

    "Principal Component 2"

)
plt.grid(

    alpha=0.3

)
plt.show()

خروجی:

تحلیل خروجی اجرای الگوریتم DIANA

نتایج اجرای الگوریتم DIANA نشان می‌دهد که داده مورد آزمایش به سه خوشه نهایی تقسیم شده است که هر کدام شامل ۴۰ نمونه هستند. در فرآیند تقسیم، ابتدا کل داده‌ها به دو گروه اصلی تقسیم شده و سپس یکی از گروه‌های حاصل که بیشترین ناهمگنی را داشته است، مجدداً تقسیم شده است. گروه‌های جداشونده (Splinter Group) در هر مرحله مسیر تشکیل ساختار سلسله‌مراتبی داده را نشان می‌دهند و مشخص می‌کنند که الگوریتم چگونه نمونه‌های مشابه را به‌تدریج از یکدیگر جدا کرده است.

معیارهای ارزیابی نیز نشان‌دهنده عملکرد مناسب الگوریتم هستند. مقدار Silhouette Score برابر با 0.8779 بیانگر جداسازی بسیار مناسب بین خوشه‌ها و انسجام بالای اعضای هر خوشه است. همچنین مقدار Adjusted Rand Index برابر با 1.0 نشان می‌دهد که خوشه‌های ایجادشده توسط DIANA کاملاً با ساختار واقعی داده مصنوعی مطابقت دارند. این نتیجه نشان می‌دهد که الگوریتم در شرایطی که داده دارای ساختار خوشه‌ای مشخص باشد، توانایی بالایی در کشف روابط سلسله‌مراتبی و تفکیک گروه‌های مختلف دارد.

جمع بندی

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

در ادامه، کتابخانه‌ها و ابزارهای مورد نیاز برای پیاده‌سازی و تحلیل الگوریتم معرفی شدند. از آنجا که DIANA در کتابخانه‌های رایج Python مانند scikit-learn به‌صورت مستقیم ارائه نشده است، یک پیاده‌سازی آموزشی از پایه توسعه داده شد تا مراحل اصلی الگوریتم شامل تشکیل ماتریس فاصله، انتخاب ناهمگن‌ترین خوشه، ایجاد گروه جداشونده (Splinter Group)، انتقال نمونه‌ها و تشکیل خوشه‌های نهایی به‌صورت شفاف قابل مشاهده و تحلیل باشد.

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

در بخش پیاده‌سازی عملی، الگوریتم روی داده مصنوعی سه‌خوشه‌ای اجرا شد و عملکرد آن با معیارهای ارزیابی خوشه‌بندی بررسی گردید. نتایج آزمایش نشان داد که الگوریتم توانست داده‌ها را به سه گروه مجزا تقسیم کند و با دستیابی به Silhouette Score برابر با 0.8779 و Adjusted Rand Index برابر با 1.0، جداسازی بسیار مناسبی میان خوشه‌ها ایجاد نماید. این نتیجه نشان‌دهنده توانایی DIANA در کشف ساختارهای پنهان داده در شرایطی است که گروه‌ها دارای مرزبندی مشخص باشند.

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

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

دکتر محمدرضا عاطفی

عضو هیئت علمی دانشگاه
رئیس هیئت مدیره گروه ناب
هم بنیان گذار شرکت دانش بنیان
مشاور شرکت ها و سازمان های بزرگ کشور

آنچه می خوانید

هوش مصنوعی

الگوریتم خوشه‌بندی کامیلیون (CHAMELEON): آموزش کامل خوشه‌بندی مبتنی بر گراف

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

توضیحات بیشتر »
هوش مصنوعی

پیاده‌سازی و تحلیل عملی الگوریتم DIANA در Python

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

توضیحات بیشتر »
هوش مصنوعی

الگوریتم DIANA چیست؟ آموزش کامل خوشه‌بندی سلسله‌مراتبی تقسیمی در یادگیری ماشین

1. اهداف یادگیری انتظار می‌رود خواننده پس از مطالعه این فصل بتواند: . 2. چکیده الگوریتم DIANA که مخفف Divisive Analysis است، یکی از مهم‌ترین روش‌های کلاسیک در خوشه‌بندی سلسله‌مراتبی تقسیمی به‌شمار می‌آید. برخلاف روش‌های تجمیعی که از نقاط منفرد آغاز می‌کنند و به‌تدریج خوشه‌ها را ادغام می‌نمایند، DIANA با

توضیحات بیشتر »