שיטות מונטה קרלו באמצעות Apache Spark

Managed Service for Apache Spark ו-Apache Spark מספקים תשתית ויכולת שבהן אפשר להשתמש כדי להריץ סימולציות של מונטה קרלו שנכתבו ב-Java, ב-Python או ב-Scala.

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

בעבר, הפעלת אלפי סימולציות הייתה יכולה לקחת הרבה זמן ולצבור עלויות גבוהות. ‫Managed Service for Apache Spark מאפשר לכם להקצות קיבולת לפי דרישה ולשלם עליה לפי דקה. ‫Apache Spark מאפשר להשתמש באשכולות של עשרות, מאות או אלפי שרתים כדי להריץ סימולציות בצורה אינטואיטיבית, וניתן להרחיב את השימוש בהם בהתאם לצרכים. המשמעות היא שתוכלו להריץ יותר סימולציות מהר יותר, וכך לעזור לעסק שלכם לחדש מהר יותר ולנהל את הסיכונים בצורה טובה יותר.

האבטחה תמיד חשובה כשעובדים עם נתונים פיננסיים. ‫Managed Service for Apache Spark פועל ב- Google Cloud, וכך עוזר לשמור על הנתונים שלכם בטוחים, מאובטחים ופרטיים בכמה דרכים. לדוגמה, כל הנתונים מוצפנים במהלך ההעברה וכשהם באחסון, ו Google Cloud הם תואמים לתקנים ISO 27001,‏ SOC3 ו-PCI.

מטרות

  • יצירת אשכול מנוהל של Managed Service for Apache Spark עם Apache Spark שכבר מותקן.
  • מריצים סימולציה של מונטה קרלו באמצעות Python כדי להעריך את הצמיחה של תיק מניות לאורך זמן.
  • הרצת סימולציית מונטה קרלו באמצעות Scala שמדמה איך קזינו מרוויח כסף.

עלויות

במסמך הזה משתמשים ברכיבים הבאים של Google Cloud, והשימוש בהם כרוך בתשלום:

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

משתמשים חדשים של Google Cloud ? יכול להיות שאתם זכאים לתקופת ניסיון בחינם.

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

לפני שמתחילים

  • הגדרת Google Cloud פרויקט
    1. נכנסים לחשבון Google Cloud . אם אתם משתמשים חדשים ב- Google Cloud, צרו חשבון כדי שתוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 300$ להרצה, לבדיקה ולפריסה של עומסי העבודה.
    2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

      Roles required to select or create a project

      • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
      • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

      Go to project selector

    3. Verify that billing is enabled for your Google Cloud project.

    4. Enable the Dataproc and Compute Engine APIs.

      Roles required to enable APIs

      To enable APIs, you need the serviceusage.services.enable permission. If you created the project, then you likely already have this permission through the Owner role (roles/owner). Otherwise, you can get this permission through the Service Usage Admin role (roles/serviceusage.serviceUsageAdmin). Learn how to grant roles.

      Enable the APIs

    5. התקינו את ה-CLI של Google Cloud.

    6. אם אתם משתמשים בספק זהויות חיצוני (IdP), קודם אתם צריכים להיכנס ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

    7. כדי לאתחל את ה-CLI של gcloud, הריצו את הפקודה הבאה:

      gcloud init
    8. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

      Roles required to select or create a project

      • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
      • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

      Go to project selector

    9. Verify that billing is enabled for your Google Cloud project.

    10. Enable the Dataproc and Compute Engine APIs.

      Roles required to enable APIs

      To enable APIs, you need the serviceusage.services.enable permission. If you created the project, then you likely already have this permission through the Owner role (roles/owner). Otherwise, you can get this permission through the Service Usage Admin role (roles/serviceusage.serviceUsageAdmin). Learn how to grant roles.

      Enable the APIs

    11. התקינו את ה-CLI של Google Cloud.

    12. אם אתם משתמשים בספק זהויות חיצוני (IdP), קודם אתם צריכים להיכנס ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

    13. כדי לאתחל את ה-CLI של gcloud, הריצו את הפקודה הבאה:

      gcloud init

יצירת אשכול Managed Service for Apache Spark

פועלים לפי השלבים כדי ליצור אשכול של Managed Service for Apache Spark ממסוף Google Cloud . הגדרות ברירת המחדל של האשכול, שכוללות שני צמתי עובדים, מספיקות ללימוד הזה.

השבתת רישום ביומן עבור אזהרות

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

שימוש ב-ssh כדי להתחבר לצומת הראשי של אשכול Managed Service for Apache Spark

לצומת הראשי של אשכול Managed Service for Apache Spark יש את הסיומת -m בשם המכונה הווירטואלית.

  1. נכנסים לדף VM instances במסוף Google Cloud .

    כניסה לדף VM instances

  2. ברשימת המכונות הווירטואליות, לוחצים על SSH בשורה של המכונה שרוצים להתחבר אליה.

    הלחצן SSH ליד שם המכונה.

ייפתח חלון SSH שמחובר לצומת הראשי.

Connected, host fingerprint: ssh-rsa 2048 ...
...
user@clusterName-m:~$

שינוי הגדרת הרישום ביומן

  1. בספריית הבית של הצומת הראשי, עורכים את /etc/spark/conf/log4j.properties.

    sudo nano /etc/spark/conf/log4j.properties
    
  2. מגדירים את log4j.rootCategory להיות שווה ל-ERROR.

    # Set only errors to be logged to the console
    log4j.rootCategory=ERROR, console
    log4j.appender.console=org.apache.log4j.ConsoleAppender
    log4j.appender.console.target=System.err
    log4j.appender.console.layout=org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n
    
  3. שומרים את השינויים ויוצאים מכלי העריכה. אם רוצים להפעיל שוב רישום מפורט (verbose) ביומן, צריך לבטל את השינוי ולהחזיר את הערך של .rootCategory לערך המקורי (INFO).

שפות תכנות ב-Spark

‫Spark תומך ב-Python,‏ Scala ו-Java כשפות תכנות לאפליקציות עצמאיות, ומספק מתורגמנים אינטראקטיביים ל-Python ול-Scala. השפה שתבחרו היא עניין של העדפה אישית. במדריך הזה נעשה שימוש במפרשים אינטראקטיביים כי אפשר להתנסות בהם על ידי שינוי הקוד, ניסיון של ערכי קלט שונים ואז הצגת התוצאות.

הערכת הצמיחה של תיק ההשקעות

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

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

  1. מפעילים את מתורגמן Python מהצומת הראשי של Managed Service for Apache Spark.

    pyspark
    

    ממתינים להנחיה של Spark >>>.

  2. מזינים את הקוד הבא. חשוב לשמור על ההזחה בהגדרת הפונקציה.

    import random
    import time
    from operator import add
    
    def grow(seed):
        random.seed(seed)
        portfolio_value = INVESTMENT_INIT
        for i in range(TERM):
            growth = random.normalvariate(MKT_AVG_RETURN, MKT_STD_DEV)
            portfolio_value += portfolio_value * growth + INVESTMENT_ANN
        return portfolio_value
    
  3. לוחצים על return עד שההנחיה של Spark מופיעה שוב.

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

    בשלב הבא תגדירו את הקבועים הנדרשים.

  4. יוצרים הרבה ערכי seed כדי להזין אותם לפונקציה. בהנחיה של Spark, מזינים את הקוד הבא, שיוצר 10,000 זרעים:

    seeds = sc.parallelize([time.time() + i for i in range(10000)])
    

    התוצאה של הפעולה parallelize היא מערך נתונים מבוזר עמיד (RDD), שהוא אוסף של רכיבים שעברו אופטימיזציה לעיבוד מקביל. במקרה הזה, ה-RDD מכיל זרעים שמבוססים על השעה הנוכחית במערכת.

    כשיוצרים את ה-RDD, ‏ Spark פורס את הנתונים על סמך מספר העובדים והליבות שזמינים. במקרה הזה, Spark בוחר להשתמש בשמונה פרוסות, פרוסה אחת לכל ליבה. זה בסדר לסימולציה הזו, שכוללת 10,000 פריטי נתונים. בסימולציות גדולות יותר, כל פרוסה עשויה להיות גדולה יותר מהמגבלה שמוגדרת כברירת מחדל. במקרה כזה, ציון פרמטר שני ל-parallelize יכול להגדיל את מספר הפרוסות, וכך לעזור לשמור על גודל פרוסה שניתן לניהול, בזמן ש-Spark עדיין מנצל את כל שמונה הליבות.

  5. מעבירים את ה-RDD שמכיל את הזרעים לפונקציית הצמיחה.

    results = seeds.map(grow)
    

    השיטה map מעבירה כל ערך התחלתי ב-RDD לפונקציה grow ומצרפת כל תוצאה ל-RDD חדש, שמאוחסן ב-results. שימו לב שהפעולה הזו, שמבצעת טרנספורמציה, לא מפיקה את התוצאות שלה באופן מיידי. ‫Spark לא יבצע את העבודה הזו עד שיהיה צורך בתוצאות. הסיבה לכך שאפשר להזין קוד בלי להגדיר את הקבועים היא הערכה עצלה.

  6. מציינים ערכים לפונקציה.

    INVESTMENT_INIT = 100000  # starting amount
    INVESTMENT_ANN = 10000  # yearly new investment
    TERM = 30  # number of years
    MKT_AVG_RETURN = 0.11 # percentage
    MKT_STD_DEV = 0.18  # standard deviation
    
  7. מתקשרים אל reduce כדי לצבור את הערכים ב-RDD. מזינים את הקוד הבא כדי לסכם את התוצאות ב-RDD:

    sum = results.reduce(add)
    
  8. אומדים ומציגים את ההחזר הממוצע:

    print (sum / 10000.)
    

    חשוב להקפיד לכלול את התו נקודה (.) בסוף. הוא מציין אריתמטיקה של נקודה צפה.

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

    MKT_AVG_RETURN = 0.07
    
  10. מריצים שוב את הסימולציה.

    print (sc.parallelize([time.time() + i for i in range(10000)]) \
            .map(grow).reduce(add)/10000.)
    
  11. כשמסיימים להתנסות, מקישים על CTRL+D כדי לצאת ממתורגמן Python.

תכנות סימולציית מונטה קרלו ב-Scala

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

  • השחקן מציב הימור שמורכב ממספר צ'יפים מתוך קרן ההימורים.
  • השחקן מטיל קובייה בעלת 100 פאות (איזה מגניב זה יכול להיות?).
  • אם התוצאה של הגלגול היא מספר מ-1 עד 49, השחקן מנצח.
  • אם התוצאה היא בין 50 ל-100, השחקן מפסיד בהתערבות.

אפשר לראות שהמשחק הזה יוצר חיסרון של אחוז אחד לשחקן: ב-51 מתוך 100 התוצאות האפשריות של כל הטלה, השחקן מפסיד.

כדי ליצור את המשחק ולהפעיל אותו:

  1. מפעילים את המפרש של Scala מהצומת הראשי של Managed Service for Apache Spark.

    spark-shell
    
  2. מעתיקים ומדביקים את הקוד הבא כדי ליצור את המשחק. ל-Scala אין את אותן דרישות כמו ל-Python בכל הנוגע להזחה, ולכן אפשר פשוט להעתיק ולהדביק את הקוד הזה בהנחיה scala>.

    val STARTING_FUND = 10
    val STAKE = 1   // the amount of the bet
    val NUMBER_OF_GAMES = 25
    
    def rollDie: Int = {
        val r = scala.util.Random
        r.nextInt(99) + 1
    }
    
    def playGame(stake: Int): (Int) = {
        val faceValue = rollDie
        if (faceValue < 50)
            (2*stake)
        else
            (0)
    }
    
    // Function to play the game multiple times
    // Returns the final fund amount
    def playSession(
       startingFund: Int = STARTING_FUND,
       stake: Int = STAKE,
       numberOfGames: Int = NUMBER_OF_GAMES):
       (Int) = {
    
        // Initialize values
        var (currentFund, currentStake, currentGame) = (startingFund, 0, 1)
    
        // Keep playing until number of games is reached or funds run out
        while (currentGame <= numberOfGames && currentFund > 0) {
    
            // Set the current bet and deduct it from the fund
            currentStake = math.min(stake, currentFund)
            currentFund -= currentStake
    
            // Play the game
            val (winnings) = playGame(currentStake)
    
            // Add any winnings
            currentFund += winnings
    
            // Increment the loop counter
            currentGame += 1
        }
        (currentFund)
    }
    
  3. לוחצים על return עד שמופיעה ההנחיה scala>.

  4. מזינים את הקוד הבא כדי לשחק במשחק 25 פעמים, שזה ערך ברירת המחדל של NUMBER_OF_GAMES.

    playSession()
    

    ההון ההתחלתי שלכם היה 10 יחידות. האם הוא גבוה או נמוך יותר עכשיו?

  5. עכשיו נדמה 10,000 שחקנים שמבצעים הימור של 100 צ'יפים בכל משחק. משחקים ב-10,000 משחקים במהלך ביקור. סימולציית מונטה קרלו הזו מחשבת את ההסתברות להפסד של כל הכסף לפני סוף הסשן. מזינים את הקוד הבא:

    (sc.parallelize(1 to 10000, 500)
      .map(i => playSession(100000, 100, 250000))
      .map(i => if (i == 0) 1 else 0)
      .reduce(_+_)/10000.0)
    

    שימו לב שהתחביר .reduce(_+_) הוא קיצור דרך ב-Scala לביצוע אגרגציה באמצעות פונקציית סיכום. היא שוות ערך מבחינת הפונקציונליות לתחביר .reduce(add) שראיתם בדוגמה של Python.

    הקוד שלמעלה מבצע את השלבים הבאים:

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

    תוצאה טיפוסית יכולה להיות:

    0.998
    

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

הסרת המשאבים

מחיקת הפרויקט

  1. במסוף Google Cloud , נכנסים לדף Manage resources.

    כניסה לדף Manage resources

  2. ברשימת הפרויקטים, בוחרים את הפרויקט שרוצים למחוק ולוחצים על Delete.
  3. כדי למחוק את הפרויקט, כותבים את מזהה הפרויקט בתיבת הדו-שיח ולוחצים על Shut down.

המאמרים הבאים