תחי ישראל - אין לנו ארץ אחרת

תחי ישראל -אין לנו ארץ אחרת

מציאת ערכים ווקטורים טיפוסיים של מטריצה עם ובלי פייתון

מחבר:
בתאריך:

וקטורים וערכים טיפוסיים מאפשרים לנו לאפיין מטריצות ולצמצם נתונים מורכבים למרכיבים חיוניים מה שעוזר בניתוח של מערכות נתונים מורכבות. במדריך זה נראה כיצד לחשב אותם בלי להשתמש בקוד עבור מערכת פשוטה וכיצד להשתמש בספריית Numpy של פייתון לפתרון מטריצות בכל רמה של מורכבות.

eigenvalues and eigenvectors

מציאת וקטורים וערכים טיפוסיים ללא קוד

נמצא את הערכים הטיפוסיים והוקטורים הטיפוסיים (eigenvalues and eigenvectors) של מטריצה שממדיה 2 X 2:

$$ A = \begin{bmatrix} 1 & 0.5 \\ 0.5 & 1 \end{bmatrix} $$

לוקטור ריבועי ייתכן וקיים צמד של וקטור וסקלר אשר מקיימים:

$$ A v = \lambda v $$

נסדר מחדש:

$$ A v - \lambda v = 0 $$

כיוון שכפל ב-I, מטריצת הזהות, כמוהו ככפל ב-1, אנו יכולים לעשות את הדבר הבא שיעזור לנו בפיתוח המשוואה האופיינית:

$$ A v - \lambda I v = 0 $$

נוציא את הוקטור v מחוץ לסוגריים:

$$ (A - \lambda I) v = 0 $$

בשלב זה, יש לנו שתי אפשרויות או שהוקטור שווה ל-0:

$$ v = 0 $$

או שהמטריצה שווה ל-0:

$$ (A - \lambda I) = 0 $$

אבל הוקטור חייב להיות שונה מ-0 (v ≠ 0) כיוון שאנחנו לא מעוניינים בפתרונות טריוויאליים.

אם המטריצה הייתה הופכית אז ניתן היה לכפול את שני האגפים בהופכי:

$$ (A - \lambda I)^{-1} (A - \lambda I) v = (A - \lambda I)^{-1} 0 $$

כדי לקבל:

$$ I v = 0 $$

כיוון שכפל במטריצת הזהות לא משנה את המטריצה, נקבל ש:

$$ v = 0 $$

קיבלנו סתירה כיוון שהוקטור v לא יכול להיות גם שווה ל-0 וגם שונה ממנו. מה שאומר שלמטריצה לא יכול להיות הופכי.

אם למטריצה אין הופכי אז הדטרמיננטה שלה בהגדרה שווה ל-0:

$$ det(A - \lambda I) = 0 $$

נציב את המטריצה שלנו כדי לקבל את הפולינום הטיפוסי:

$$ det( \begin{bmatrix} 1 & 0.5 \\ 0.5 & 1 \end{bmatrix} - \lambda \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}) = 0 $$

$$ det( \begin{bmatrix} 1 & 0.5 \\ 0.5 & 1 \end{bmatrix} - \begin{bmatrix} \lambda & 0 \\ 0 & \lambda \end{bmatrix}) = 0 $$

$$ det( \begin{bmatrix} 1-\lambda & 0.5 \\ 0.5 & 1-\lambda \end{bmatrix} ) = 0 $$

נשתמש באלגוריתם לפתרון דטרמיננטה של 2X2 כדי להפיק משוואה טיפוסית:

$$ \lambda^2 - 2\lambda + 0.75 = 0 $$

נפתור את המשוואה כדי לקבל את הערכים הטיפוסיים:

$$ \lambda_{1,2} = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} $$

$$ \lambda_{1,2} = \frac{2 \pm \sqrt{4 - 3}}{2} $$

הצבה ופתרון המשוואה נותנת את הערכים הטיפוסיים:

$$ \lambda_1 = 1.5 \\ \lambda_2 = 0.5 $$

את הווקטורים הטיפוסיים נמצא על ידי הצבת ערכי הערכים הטיפוסיים חזרה לתוך המשוואה הטיפוסית:

$$ (A - \lambda I) v = 0 $$

$$ E_{1.5} = \begin{bmatrix} 1-1.5 & 0.5 \\ 0.5 & 1-1.5 \end{bmatrix} \begin{bmatrix} x_{1} \\ x_{2} \end{bmatrix} = \begin{bmatrix} 0 \\ 0 \end{bmatrix} $$

כיוון ששתי השורות נותנות את אותה התוצאה אפשר להשתמש רק באחת מהם בשביל הפתרון. נבחר בעליונה.

נכפול את המטריצות, ונסדר את התוצאה:

$$ x_{1} = x_{2} $$

מה שאומר שהוקטור הטיפוסי הוא משפחה של פתרונות שבו האיברים הראשון והשני שווים. כך נציין את זה:

$$ E_{1.5} = span( \begin{bmatrix} 1 \\ 1 \end{bmatrix} ) $$

נמצא את הוקטור האופייני השני באותו האופן, ונקבל:

$$ E_{0.5} = span( \begin{bmatrix} 1 \\ -1 \end{bmatrix} ) $$

נבדוק את התוצאה של הראשון האם מתקיים השיוויון:

$$ A v = \lambda v $$

$$ \begin{bmatrix} 1 & 0.5 \\ 0.5 & 1 \end{bmatrix} \begin{bmatrix} 1 \\ 1 \end{bmatrix} = 1.5 \begin{bmatrix} 1 \\ 1 \end{bmatrix} $$

והשיוויון אכן מתקיים.

את אותה הבדיקה אפשר לעשות כדי לוודא את הקומבינציה של הערך והוקטור הטיפוסי האחרים שקיבלנו.

 

שימוש בפייתון למציאת ערכים ווקטורים טיפוסיים של מטריצה

אפשר להשתמש בפייתון למציאת ערכים ווקטורים טיפוסיים של מטריציה. אפשרות מועילה ביותר ביישומים אמיתיים בהם המכונה צריכה לפתור מטריצות מורכבות כדי לספק תשובה.

לצורך הפתרון נשתמש בספריית Numpy:

import numpy as np

# Matrix formatted as a Numpy array of arrays
A = np.array([[1, 0.5], [0.5, 1]])

# This is where the magic happens
lamdas, V = np.linalg.eig(A)

# Print the typical values
print(lamdas)

# Print the typical vectors
print(V)

הערכים הטיפוסיים המחושבים הם:

[1.5 0.5]

הוקטורים הטיפוסיים המחושבים הם:

[[0.70710678 0.70710678]
 [-0.70710678 0.70710678]]
  • העמודה הראשונה היא משפחת הפתרונות עבור הערך הטיפוסי λ = 1.5 כאשר כל הוקטורים במשפחה מקיימים שויון בין הערך הראשון והשני, משמע:

    $$ E = span( \begin{bmatrix} 1 \\ 1 \end{bmatrix} ) $$

  • העמודה השנייה היא משפחת הפתרונות עבור הערך הטיפוסי λ = 0.5 כאשר כל הווקטורים במשפחה מקיימים שהערך השני הוא השלילי של הראשון:

    $$ E = span( \begin{bmatrix} 1 \\ -1 \end{bmatrix} ) $$

נוודא:

# Let's confirm
# Eigenvectors
v = V[:, 0]

# Eigenvalues
lamda = lamdas[0]

np.dot(A, v) == lamda * v
# print(np.dot(A, v))
# print(lamda * v)

התוצאה:

array([ True,  True])
  • השיוויון אכן מתקיים.

 

קוד להצגת הטרנספורמציות כולל האייגנים

קוד הפייתון הבא שימש אותי להצגת הלפני ואחרי של הטרנספורמציה כולל גודל וכיוון האייגנוקטורים:


import numpy as np
import matplotlib.pyplot as plt
from numpy.linalg import eig

# Define the coordinates
coords_before = [[1, 0], [0, 1]]
coords_after = [[1,0.5],[0.5,1]]
coords_list = [coords_before, coords_after]

grid_size = 100

def generate_dot_grid_coordinates(grid_size, spacing=6):
    """Generate coordinates for a grid of dots in a given size with specified spacing with the center at [0,0].
       Also generates a gradient based on the distance from the center."""

    # Calculate half the grid size
    half_size = grid_size // 2

    # Create x and y ranges
    x = np.arange(-half_size, half_size + spacing, spacing)
    y = np.arange(-half_size, half_size + spacing, spacing)

    # Generate grid coordinate matrices
    xv, yv = np.meshgrid(x, y)

    # Flatten and stack the coordinates
    coords = np.vstack([xv.ravel(), yv.ravel()])

    # Create the gradient based on the x and y coordinates
    gradient_x = (coords[0] - np.min(coords[0])) / (np.max(coords[0]) - np.min(coords[0]))
    gradient_y = (coords[1] - np.min(coords[1])) / (np.max(coords[1]) - np.min(coords[1]))

    # Combine the gradients
    gradient = (gradient_x + gradient_y) / 2

    return coords, gradient

def plot_vectors(vectors, colors):
    plt.axvline(x=0, color='lightgray')
    plt.axhline(y=0, color='lightgray')

    for i in range(len(vectors)):
        x = np.concatenate([[0, 0], vectors[i]])
        plt.quiver([x[0]], [x[1]], [x[2]], [x[3]],
                   angles='xy', scale_units='xy', scale=1, color=colors[i])

# Generate grid coordinates and gradient
coords, gradient = generate_dot_grid_coordinates(grid_size)

# Loop through the coordinates and plot eigenvectors
for i, coord in enumerate(coords_list):
    # Transform the grid coordinates using the transition matrix `coord`
    transformed_coords = np.dot(coord, coords)

    # Calculate the limits for the plot
    x_min, x_max = np.min(transformed_coords[0]) - 5, np.max(transformed_coords[0]) + 5
    y_min, y_max = np.min(transformed_coords[1]) - 5, np.max(transformed_coords[1]) + 5

    # Create the plot
    plt.figure()

    # Plot the transformed grid as background
    plt.scatter(transformed_coords[0], transformed_coords[1], c=gradient, cmap='ocean', s=10, alpha=0.6)

    # Calculate eigenvalues and eigenvectors
    eigenvalues, eigenvectors = eig(coord)

    # Plot eigenvectors
    scale_factor = grid_size / 2
    eigenvector = eigenvectors[:, 0]
    magnitude = np.sqrt(np.abs(eigenvalues[0]))
    red_vector = [scale_factor * magnitude * eigenvector[0], scale_factor * magnitude * eigenvector[1]]

    eigenvector = eigenvectors[:, 1]
    magnitude = np.sqrt(np.abs(eigenvalues[1]))
    blue_vector = [scale_factor * magnitude * eigenvector[0], scale_factor * magnitude * eigenvector[1]]

    plot_vectors([red_vector, blue_vector], ["red", "blue"])

    # Print eigenvalues and eigenvectors
    print(f"λ1 = {np.round(eigenvalues[0], 2)}")
    print(f"λ2 = {np.round(eigenvalues[1], 2)}")
    print(f"det(A) = {np.linalg.det(coord)}")
    print(eigenvectors[:, 0])
    print(eigenvectors[:, 1])

    # Set plot limits dynamically
    plt.xlim(x_min, x_max)
    plt.ylim(y_min, y_max)
    plt.show()

התוצאה:

eigenvalues and eigenvectors

* הקוד עובד כל עוד התוצאה היא מספרים אמיתיים.

  • אפשר לראות שהטנספורמציה מאריכה את הוקטור הטיפוסי האדום בשיעור של פי-1.5 ומכווצת את הוקטור הטיפוסי הכחול פי 0.75. סה"כ הטרנספורמציה מכווצת את השטח ל-75% מהמקור כפי שאפשר ללמוד מחישוב הדטרמיננטה: det(A) = 0.75 .

 

למדריכים נוספים בסדרה על למידת מכונה

12 דברים שאתה חייב לדעת כשאתה עובד עם ספריית Numpy של Python

למידת מכונה בלתי מפוקחת באמצעות PCA

מציאת יחס הזהב בתוך סדרת מספרים עולה באמצעות (קצת) אלגברה לינארית

 

לכל המדריכים בנושא של למידת מכונה

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים

 

 

המדריכים באתר עוסקים בנושאי תכנות ופיתוח אישי. הקוד שמוצג משמש להדגמה ולצרכי לימוד. התוכן והקוד המוצגים באתר נבדקו בקפידה ונמצאו תקינים. אבל ייתכן ששימוש במערכות שונות, דוגמת דפדפן או מערכת הפעלה שונה ולאור השינויים הטכנולוגיים התכופים בעולם שבו אנו חיים יגרום לתוצאות שונות מהמצופה. בכל מקרה, אין בעל האתר נושא באחריות לכל שיבוש או שימוש לא אחראי בתכנים הלימודיים באתר.

למרות האמור לעיל, ומתוך רצון טוב, אם נתקלת בקשיים ביישום הקוד באתר מפאת מה שנראה לך כשגיאה או כחוסר עקביות נא להשאיר תגובה עם פירוט הבעיה באזור התגובות בתחתית המדריכים. זה יכול לעזור למשתמשים אחרים שנתקלו באותה בעיה ואם אני רואה שהבעיה עקרונית אני עשוי לערוך התאמה במדריך או להסיר אותו כדי להימנע מהטעיית הציבור.

שימו לב! הסקריפטים במדריכים מיועדים למטרות לימוד בלבד. כשאתם עובדים על הפרויקטים שלכם אתם צריכים להשתמש בספריות וסביבות פיתוח מוכחות, מהירות ובטוחות.

המשתמש באתר צריך להיות מודע לכך שאם וכאשר הוא מפתח קוד בשביל פרויקט הוא חייב לשים לב ולהשתמש בסביבת הפיתוח המתאימה ביותר, הבטוחה ביותר, היעילה ביותר וכמובן שהוא צריך לבדוק את הקוד בהיבטים של יעילות ואבטחה. מי אמר שלהיות מפתח זו עבודה קלה ?

השימוש שלך באתר מהווה ראייה להסכמתך עם הכללים והתקנות שנוסחו בהסכם תנאי השימוש.

הוסף תגובה חדשה

 

 

ענה על השאלה הפשוטה הבאה כתנאי להוספת תגובה:

איך אומרים בעברית אינטרנט?