Solutech Engineering
Innovations/Suggested Products

Machine Learning Implementation

Detailed explanation of the machine learning algorithms and implementation

Machine Learning Implementation

The Suggested Products feature leverages advanced machine learning techniques to provide intelligent product recommendations. This section explores the algorithms, implementation details, and optimization strategies used in the system.

Algorithm Overview

The system employs two complementary machine learning approaches:

  1. Apriori Algorithm: For discovering frequent item sets and association rules
  2. Recent Pattern Analysis: For real-time behavioral pattern recognition

Apriori Algorithm Implementation

What is the Apriori Algorithm?

The Apriori algorithm is a classic algorithm in machine learning used for frequent item set mining and association rule learning. It identifies frequent patterns in transactional data and generates association rules between different items.

graph TD
    A[Transaction Data] --> B[Frequent 1-itemsets]
    B --> C[Frequent 2-itemsets]
    C --> D[Frequent 3-itemsets]
    D --> E[Association Rules]
    E --> F[Product Recommendations]
    
    subgraph "Support Filtering"
        B1[Count Item Frequency]
        B2[Apply Minimum Support]
        B1 --> B2
    end
    
    subgraph "Confidence Calculation"
        C1[Generate Candidate Rules]
        C2[Calculate Confidence]
        C3[Apply Minimum Confidence]
        C1 --> C2 --> C3
    end

Key Concepts

Support

The proportion of transactions that contain a particular item set.

Support(A) = Number of transactions containing A / Total number of transactions

Example: If 100 out of 1000 customers buy products A and B together, then:

Support(A,B) = 100/1000 = 0.1 (10%)

Confidence

The likelihood that a transaction containing item A also contains item B.

Confidence(A → B) = Support(A,B) / Support(A)

Example: If 80% of customers who buy product A also buy product B:

Confidence(A → B) = 0.8 (80%)

Lift

Measures how much more likely item B is purchased when item A is purchased.

Lift(A → B) = Confidence(A → B) / Support(B)

Implementation Details

PHP-ML Integration

The system uses the Phpml\Association\Apriori library for implementation:

use Phpml\Association\Apriori;

class GenerateSuggestedProductsService
{
    private $support = 0.1;      // Minimum support threshold
    private $confidence = 0.5;   // Minimum confidence threshold
    
    public function apply_apriori($customer_orders)
    {
        // Initialize Apriori algorithm
        $associator = new Apriori($this->support, $this->confidence);
        
        // Prepare transaction data
        $transactions = $this->prepareTransactions($customer_orders);
        
        // Train the model
        $associator->train($transactions['products'], $transactions['labels']);
        
        // Generate frequent item sets
        $frequentSets = $associator->apriori();
        
        // Convert to association rules
        $rules = $associator->getRules();
        
        return $this->processResults($frequentSets, $rules);
    }
    
    private function prepareTransactions($orders)
    {
        $products = [];
        $labels = [];
        
        foreach ($orders as $order) {
            $orderProducts = [];
            foreach ($order['products'] as $product) {
                $orderProducts[] = $product['id'];
            }
            $products[] = $orderProducts;
            $labels[] = $order['customer_id'];
        }
        
        return ['products' => $products, 'labels' => $labels];
    }
}

Data Processing Pipeline

graph LR
    A[Raw Orders] --> B[Data Validation]
    B --> C[Customer Grouping]
    C --> D[Transaction Formation]
    D --> E[Support Calculation]
    E --> F[Frequent Itemsets]
    F --> G[Rule Generation]
    G --> H[Confidence Filtering]
    H --> I[Final Recommendations]

Algorithm Workflow

Step 1: Data Collection and Preparation

public function getOrders($dates)
{
    $query = DB::table($this->ordersTable)
        ->join($this->orderDetailsTable, 
               $this->ordersTable.'.id', '=', 
               $this->orderDetailsTable.'.'.$this->orderIdField)
        ->whereBetween($this->ordersTable.'.created_at', $dates)
        ->where($this->ordersTable.'.status', '!=', 'cancelled')
        ->select([
            $this->ordersTable.'.'.$this->customerIdField.' as customer_id',
            $this->orderDetailsTable.'.product_id',
            $this->orderDetailsTable.'.quantity',
            $this->ordersTable.'.created_at'
        ]);
    
    return $query->get()->groupBy('customer_id');
}

Step 2: Customer Segmentation

public function customers($orders)
{
    $customers = collect();
    
    foreach ($orders as $customerId => $customerOrders) {
        // Filter customers with minimum order frequency
        if (count($customerOrders) >= $this->minOrderCount) {
            $customers->push([
                'customer_id' => $customerId,
                'orders' => $customerOrders,
                'total_orders' => count($customerOrders),
                'unique_products' => $customerOrders->pluck('product_id')->unique()->count()
            ]);
        }
    }
    
    return $customers;
}

Step 3: Transaction Processing

private function processCustomerOrders($customer)
{
    $transactions = [];
    
    // Group orders by date to form transactions
    $ordersByDate = $customer['orders']->groupBy(function($order) {
        return Carbon::parse($order->created_at)->format('Y-m-d');
    });
    
    foreach ($ordersByDate as $date => $dayOrders) {
        $productIds = $dayOrders->pluck('product_id')->unique()->values()->toArray();
        
        if (count($productIds) >= 2) { // Minimum 2 products for association
            $transactions[] = $productIds;
        }
    }
    
    return $transactions;
}

Step 4: Apriori Algorithm Application

public function generateFrequentItemsets($transactions)
{
    $apriori = new Apriori($this->support, $this->confidence);
    
    // Generate frequent 1-itemsets
    $frequentItemsets = [];
    $itemCounts = [];
    
    // Count individual items
    foreach ($transactions as $transaction) {
        foreach ($transaction as $item) {
            $itemCounts[$item] = ($itemCounts[$item] ?? 0) + 1;
        }
    }
    
    // Apply minimum support
    $totalTransactions = count($transactions);
    $minSupport = $this->support * $totalTransactions;
    
    foreach ($itemCounts as $item => $count) {
        if ($count >= $minSupport) {
            $frequentItemsets[1][] = [$item];
        }
    }
    
    // Generate higher-order itemsets
    $k = 2;
    while (!empty($frequentItemsets[$k - 1])) {
        $candidates = $this->generateCandidates($frequentItemsets[$k - 1]);
        $frequentItemsets[$k] = $this->pruneInfrequent($candidates, $transactions, $minSupport);
        $k++;
    }
    
    return $frequentItemsets;
}

Algorithm Parameters and Tuning

Support Threshold Configuration

// Configuration in config/eva.php
'suggested_products_apriori_support' => env('SUGGESTED_PRODUCTS_APRIORI_SUPPORT', 0.1),

Impact of Support Values:

SupportEffectUse Case
0.05More itemsets, potentially noisyLarge diverse catalogs
0.1Balanced approachGeneral purpose
0.2Fewer, more reliable itemsetsSpecialized catalogs
0.3+Very conservative, few resultsHigh-confidence only

Confidence Threshold Configuration

'suggested_products_confidence_support' => env('SUGGESTED_PRODUCTS_CONFIDENCE_SUPPORT', 0.5),

Impact of Confidence Values:

ConfidenceEffectInterpretation
0.3Liberal recommendations30% likelihood
0.5Moderate confidence50% likelihood
0.7High confidence70% likelihood
0.9+Very conservative90%+ likelihood

Performance Optimizations

Memory Management

public function processInChunks($customers)
{
    $chunkSize = config('eva.suggested_products_chunk_size', 30);
    
    $customers->chunk($chunkSize)->each(function ($chunk) {
        foreach ($chunk as $customer) {
            try {
                $this->processCustomer($customer);
            } catch (OutOfMemoryError $e) {
                Log::error("Memory limit exceeded for customer {$customer['id']}");
                $this->logError($e->getMessage());
            }
        }
        
        // Force garbage collection
        gc_collect_cycles();
    });
}

Algorithm Efficiency Improvements

// Early termination for sparse data
private function shouldContinueProcessing($transactions)
{
    $averageItemsPerTransaction = array_sum(array_map('count', $transactions)) / count($transactions);
    $uniqueItems = count(array_unique(array_merge(...$transactions)));
    
    // Don't process if data is too sparse
    return $averageItemsPerTransaction >= 2 && $uniqueItems >= 5;
}

// Optimized candidate generation
private function generateCandidates($frequentItemsets)
{
    $candidates = [];
    $count = count($frequentItemsets);
    
    for ($i = 0; $i < $count - 1; $i++) {
        for ($j = $i + 1; $j < $count; $j++) {
            $candidate = array_unique(array_merge($frequentItemsets[$i], $frequentItemsets[$j]));
            
            // Prune if any subset is not frequent
            if ($this->allSubsetsFrequent($candidate, $frequentItemsets)) {
                $candidates[] = $candidate;
            }
        }
    }
    
    return $candidates;
}

Recent Pattern Analysis

Algorithm Description

The recent pattern analysis complements the Apriori algorithm by focusing on immediate customer behavior rather than historical patterns.

graph TD
    A[Customer Request] --> B[Fetch Recent Orders]
    B --> C[Extract Order Patterns]
    C --> D[Calculate Frequencies]
    D --> E[Apply Business Rules]
    E --> F[Generate Suggestions]
    
    subgraph "Pattern Recognition"
        C1[Order Cycles]
        C2[Product Frequencies]
        C3[Quantity Patterns]
        C4[Timing Analysis]
    end
    
    subgraph "Business Logic"
        E1[Price Lists]
        E2[Stock Availability]
        E3[Customer Preferences]
        E4[Seasonal Adjustments]
    end

Implementation

class GetSuggestedProductsFromRecentOrdersService
{
    public function getSuggestions($customerId, $options = [])
    {
        $recentOrders = $this->getRecentOrders($customerId, $options);
        $patterns = $this->analyzePatterns($recentOrders);
        $suggestions = $this->generateSuggestions($patterns, $customerId);
        
        return $this->enrichWithBusinessData($suggestions, $customerId);
    }
    
    private function analyzePatterns($orders)
    {
        $patterns = [];
        
        foreach ($orders as $order) {
            foreach ($order->details as $detail) {
                $productId = $detail->product_id;
                
                if (!isset($patterns[$productId])) {
                    $patterns[$productId] = [
                        'frequencies' => [],
                        'quantities' => [],
                        'intervals' => []
                    ];
                }
                
                $patterns[$productId]['frequencies'][] = 1;
                $patterns[$productId]['quantities'][] = $detail->quantity;
                $patterns[$productId]['intervals'][] = $order->created_at;
            }
        }
        
        return $this->calculateStatistics($patterns);
    }
    
    private function calculateStatistics($patterns)
    {
        foreach ($patterns as $productId => &$pattern) {
            $pattern['frequency_score'] = count($pattern['frequencies']);
            $pattern['avg_quantity'] = array_sum($pattern['quantities']) / count($pattern['quantities']);
            $pattern['consistency_score'] = $this->calculateConsistency($pattern['intervals']);
            $pattern['trend_score'] = $this->calculateTrend($pattern['quantities']);
        }
        
        return $patterns;
    }
}

Machine Learning Metrics

Evaluation Metrics

class ModelEvaluator
{
    public function calculateAccuracy($predictions, $actualOrders)
    {
        $correct = 0;
        $total = count($predictions);
        
        foreach ($predictions as $prediction) {
            if ($this->isProductOrdered($prediction['product_id'], $actualOrders)) {
                $correct++;
            }
        }
        
        return $total > 0 ? $correct / $total : 0;
    }
    
    public function calculatePrecision($predictions, $actualOrders)
    {
        $truePositives = 0;
        $falsePositives = 0;
        
        foreach ($predictions as $prediction) {
            if ($this->isProductOrdered($prediction['product_id'], $actualOrders)) {
                $truePositives++;
            } else {
                $falsePositives++;
            }
        }
        
        return ($truePositives + $falsePositives) > 0 ? 
               $truePositives / ($truePositives + $falsePositives) : 0;
    }
    
    public function calculateRecall($predictions, $actualOrders)
    {
        $predictedProducts = collect($predictions)->pluck('product_id');
        $actualProducts = collect($actualOrders)->pluck('product_id');
        
        $truePositives = $predictedProducts->intersect($actualProducts)->count();
        $falseNegatives = $actualProducts->diff($predictedProducts)->count();
        
        return ($truePositives + $falseNegatives) > 0 ? 
               $truePositives / ($truePositives + $falseNegatives) : 0;
    }
}

Advanced Features

Seasonal Adjustment

private function applySeasonalAdjustment($suggestions)
{
    $currentMonth = now()->month;
    $seasonalFactors = config('eva.seasonal_factors', []);
    
    foreach ($suggestions as &$suggestion) {
        $productCategory = $suggestion['product_category'];
        
        if (isset($seasonalFactors[$productCategory][$currentMonth])) {
            $factor = $seasonalFactors[$productCategory][$currentMonth];
            $suggestion['adjusted_quantity'] = $suggestion['quantity'] * $factor;
            $suggestion['seasonal_factor'] = $factor;
        }
    }
    
    return $suggestions;
}

Customer Segmentation

private function getCustomerSegment($customerId)
{
    $customer = Customer::find($customerId);
    
    // RFM Analysis (Recency, Frequency, Monetary)
    $recency = $this->calculateRecency($customer);
    $frequency = $this->calculateFrequency($customer);
    $monetary = $this->calculateMonetary($customer);
    
    // Segment based on RFM scores
    if ($recency >= 4 && $frequency >= 4 && $monetary >= 4) {
        return 'champions';
    } elseif ($recency >= 3 && $frequency >= 3) {
        return 'loyal_customers';
    } elseif ($recency >= 4) {
        return 'new_customers';
    } else {
        return 'at_risk';
    }
}

Model Performance and Monitoring

Performance Tracking

class PerformanceTracker
{
    public function trackRecommendationAccuracy()
    {
        $metrics = [
            'total_recommendations' => 0,
            'accepted_recommendations' => 0,
            'conversion_rate' => 0,
            'avg_order_value_increase' => 0
        ];
        
        // Calculate metrics from actual vs predicted orders
        $results = DB::table('order_recommendations')
            ->join('orders', 'order_recommendations.customer_id', '=', 'orders.customer_id')
            ->where('orders.created_at', '>=', now()->subDays(30))
            ->get();
            
        foreach ($results as $result) {
            $metrics['total_recommendations']++;
            
            if ($this->wasRecommendationAccepted($result)) {
                $metrics['accepted_recommendations']++;
            }
        }
        
        $metrics['conversion_rate'] = $metrics['total_recommendations'] > 0 ? 
            $metrics['accepted_recommendations'] / $metrics['total_recommendations'] : 0;
            
        return $metrics;
    }
}

A/B Testing Framework

class RecommendationABTest
{
    public function assignTestGroup($customerId)
    {
        $hash = crc32($customerId . config('app.key'));
        return ($hash % 100) < 50 ? 'control' : 'treatment';
    }
    
    public function getRecommendations($customerId, $context)
    {
        $group = $this->assignTestGroup($customerId);
        
        if ($group === 'control') {
            return $this->getBaselineRecommendations($customerId);
        } else {
            return $this->getMLRecommendations($customerId, $context);
        }
    }
    
    public function trackConversion($customerId, $recommendedProducts, $actualOrder)
    {
        ABTestResult::create([
            'customer_id' => $customerId,
            'test_group' => $this->assignTestGroup($customerId),
            'recommended_products' => json_encode($recommendedProducts),
            'actual_order' => json_encode($actualOrder),
            'conversion_rate' => $this->calculateConversion($recommendedProducts, $actualOrder)
        ]);
    }
}

This comprehensive machine learning implementation provides a robust foundation for intelligent product recommendations, combining proven algorithms with modern optimization techniques and monitoring capabilities.