导航

dmitryikh/leaves LightGBM 的go版本例子

Posted on 2022-11-07 14:50  蝈蝈俊  阅读(304)  评论(3编辑  收藏  举报

package ml

import (
	"github.com/dmitryikh/leaves"
)

var modelML *leaves.Ensemble

// 初始化加载模型
func initML() {
	if modelML != nil {
		return
	}

	useTransformation := true
	var err error
	modelML, err = leaves.LGEnsembleFromFile("./model.txt", useTransformation)
	if err != nil {
		panic(err)
	}
}

// 预测
func Predict(fvals []float64) bool {
	initML()

	p := modelML.PredictSingle(fvals, 0)
	return p > 0.5
}


参考: LightGBM简单例子

进一步封装代码


package analysis

import (
	"bytes"
	"fmt"
	"math/rand"
	"analysis/ml"
	"strings"
	"time"
)

// 机器学习模型使用的特征
// 数据源改成MOD的新版本特征集合
// 带 vs 的都是比率
type Features2 struct {
	DebugInfo                          string  // 调试时帮助定位的信息,比如:时间
	QPM                                int64   // 当前的QPM
	QPMvsYesterday                     float64 // 日环比  比率
	QPMvsYesterdayMax                  float64 // 当前QPM比前日Max QPM的百分比 比率
	QPMvsWeek                          float64 // 周同比  比率
	QPMvsMonth                         float64 // 月同比  比率
	QPMvsPrevMinute                    float64 // QPM 当前分钟 vs 前一分钟 比率
	QPMvsLast20AvgFluctuation          float64 // QPM 当前分钟 vs 前20分钟的平均 比率
	AVGIncrease                        int64   // 比前一分钟平均耗时增加的毫秒数
	AVGLast20Increase                  float64 // 比前20分钟平均耗时增加的毫秒数
	AVGvsPrevMinute                    float64 // 平均耗时 当前分钟  vs 前一分钟 比率
	AVGvsLast20AvgFluctuation          float64 // 平均耗时 当前分钟 vs 前20分钟的平均 比率
	ThroughputvsPrevMinute             float64 // 吞吐量 当前分钟 vs 前一分钟 比率
	ThroughputvsLast20AvgFluctuation   float64 // 吞吐量 当前分钟 vs 前20分钟的平均 比率
	ErrorNum                           int     // 错误数
	ErrorvsPrevMinute                  float64 // 错误   当前分钟  vs 前一分钟
	CPUBusyMax                         float64 // cpu.busy 最大值
	CPUBusyOverThreshold               int     // cpu.busy 超过阈值的数量  阈值 > 80%
	JVMFullgcCountMax                  float64 // jvm.fullgc.count 最大值
	JVMFullgcCountOverThreshold        int     // jvm.fullgc.count 超过阈值的数量 	阈值:> 0
	JVMThreadBlockedCountMax           float64 // jvm.thread.blocked.count 最大值
	JVMThreadBlockedCountOverThreshold int     // jvm.thread.blocked.count 超过阈值的数量  阈值 > 3
	MEMUsedPercentMax                  float64 // mem.memused.percent 最大值
	MEMUsedPercentOverThreshold        int     // mem.memused.percent 超过阈值的数量  阈值 > 80%
	MEMSwapusedCountMax                float64 // mem.swapused.percent 最大值
	MEMSwapusedCountOverThreshold      int     // mem.swapused.percent 超过阈值的数量  阈值 > 80%
	NumberOfMachines                   int     // 机器数, 有host数据的机器数

}

func GetCSVHeader(debug bool) string {
	doc := strings.Builder{}
	if debug {
		doc.WriteString("debug,")
	}
	doc.WriteString("label,")
	doc.WriteString("QPM,")
	doc.WriteString("QPMvsYesterday,")
	doc.WriteString("QPMvsYesterdayMax,")
	doc.WriteString("QPMvsWeek,")
	doc.WriteString("QPMvsMonth,")
	doc.WriteString("QPMvsPrevMinute,")
	doc.WriteString("QPMvsLast20AvgFluctuation,")
	doc.WriteString("AVGIncrease,")
	doc.WriteString("AVGLast20Increase,")
	doc.WriteString("AVGvsPrevMinute,")
	doc.WriteString("AVGvsLast20AvgFluctuation,")
	doc.WriteString("ThroughputvsPrevMinute,")
	doc.WriteString("ThroughputvsLast20AvgFluctuation,")
	doc.WriteString("ErrorNum,")
	doc.WriteString("ErrorvsPrevMinute,")
	doc.WriteString("CPUBusyMax,")
	doc.WriteString("CPUBusyOverThreshold,")
	doc.WriteString("JVMFullgcCountMax,")
	doc.WriteString("JVMFullgcCountOverThreshold,")
	doc.WriteString("JVMThreadBlockedCountMax,")
	doc.WriteString("JVMThreadBlockedCountOverThreshold,")
	doc.WriteString("MEMUsedPercentMax,")
	doc.WriteString("MEMUsedPercentOverThreshold,")
	doc.WriteString("MEMSwapusedCountMax,")
	doc.WriteString("MEMSwapusedCountOverThreshold,")
	doc.WriteString("NumberOfMachines")
	doc.WriteString("\n")

	return doc.String()
}

// 生成 csv 行
func (f *Features2) CSV(label int, debug bool) string {
	doc := strings.Builder{}
	doc.WriteString(fmt.Sprintf("%d,", label))

	// if debug {
	// 	doc.WriteString(f.DebugInfo)
	// 	doc.WriteString(",")
	// }

	if f.QPM > 0 {
		doc.WriteString(fmt.Sprintf("%d,", f.QPM))
	} else {
		doc.WriteString("0,")
	}

	if f.QPMvsYesterday > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.QPMvsYesterday))
	} else {
		doc.WriteString("0,")
	}

	if f.QPMvsYesterdayMax > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.QPMvsYesterdayMax))
	} else {
		doc.WriteString("0,")
	}
	if f.QPMvsWeek > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.QPMvsWeek))
	} else {
		doc.WriteString("0,")
	}

	if f.QPMvsMonth > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.QPMvsMonth))
	} else {
		doc.WriteString("0,")
	}

	if f.QPMvsPrevMinute > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.QPMvsPrevMinute))
	} else {
		doc.WriteString("0,")
	}

	if f.QPMvsLast20AvgFluctuation > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.QPMvsLast20AvgFluctuation))
	} else {
		doc.WriteString("0,")
	}

	doc.WriteString(fmt.Sprintf("%d,", f.AVGIncrease))

	if f.AVGLast20Increase > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.AVGLast20Increase))
	} else {
		doc.WriteString("0,")
	}

	if f.AVGvsPrevMinute > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.AVGvsPrevMinute))
	} else {
		doc.WriteString("0,")
	}

	if f.AVGvsLast20AvgFluctuation > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.AVGvsLast20AvgFluctuation))
	} else {
		doc.WriteString("0,")
	}

	if f.ThroughputvsPrevMinute > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.ThroughputvsPrevMinute))
	} else {
		doc.WriteString("0,")
	}

	if f.ThroughputvsLast20AvgFluctuation > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.ThroughputvsLast20AvgFluctuation))
	} else {
		doc.WriteString("0,")
	}

	if f.ErrorNum > 0 {
		doc.WriteString(fmt.Sprintf("%d,", f.ErrorNum))
	} else {
		doc.WriteString("0,")
	}

	if f.ErrorvsPrevMinute > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.ErrorvsPrevMinute))
	} else {
		doc.WriteString("0,")
	}

	if f.CPUBusyMax > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.CPUBusyMax))
	} else {
		doc.WriteString("0,")
	}

	doc.WriteString(fmt.Sprintf("%d,", f.CPUBusyOverThreshold))

	if f.JVMFullgcCountMax > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.JVMFullgcCountMax))
	} else {
		doc.WriteString("0,")
	}

	doc.WriteString(fmt.Sprintf("%d,", f.JVMFullgcCountOverThreshold))

	if f.JVMThreadBlockedCountMax > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.JVMThreadBlockedCountMax))
	} else {
		doc.WriteString("0,")
	}

	doc.WriteString(fmt.Sprintf("%d,", f.JVMThreadBlockedCountOverThreshold))

	if f.MEMUsedPercentMax > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.MEMUsedPercentMax))
	} else {
		doc.WriteString("0,")
	}

	doc.WriteString(fmt.Sprintf("%d,", f.MEMUsedPercentOverThreshold))

	if f.MEMSwapusedCountMax > float64(0.000001) {
		doc.WriteString(fmt.Sprintf("%.6f,", f.MEMSwapusedCountMax))
	} else {
		doc.WriteString("0,")
	}

	doc.WriteString(fmt.Sprintf("%d,", f.MEMSwapusedCountOverThreshold))

	doc.WriteString(fmt.Sprintf("%d", f.NumberOfMachines))
	doc.WriteString("\n")

	return doc.String()
}

// 产生一行 LibSVM 格式的数据
// 格式规范看
// https://blog.csdn.net/yangshaojun1992/article/details/87861767
// label 0 没有问题 1 有问题
func (f *Features2) LibSVM(label int, debug bool) string {
	doc := bytes.NewBufferString(fmt.Sprintf("%d", label))
	if debug {
		doc.WriteString(fmt.Sprintf(" %s", f.DebugInfo))
	}

	if f.QPM > 0 {
		if debug {
			doc.WriteString(fmt.Sprintf(" 1-QPM:%d", f.QPM))
		} else {
			doc.WriteString(fmt.Sprintf(" 1:%d", f.QPM))
		}
	} else {
		doc.WriteString(" 1:0")
	}

	if f.QPMvsYesterday > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 2-QPMvsYesterday:%.6f", f.QPMvsYesterday))
		} else {
			doc.WriteString(fmt.Sprintf(" 2:%.6f", f.QPMvsYesterday))
		}
	} else {
		doc.WriteString(" 2:0")
	}

	if f.QPMvsYesterdayMax > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 3-QPMvsYesterdayMax:%.6f", f.QPMvsYesterdayMax))
		} else {
			doc.WriteString(fmt.Sprintf(" 3:%.6f", f.QPMvsYesterdayMax))
		}
	} else {
		doc.WriteString(" 3:0")
	}

	if f.QPMvsWeek > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 4-QPMvsWeek:%.6f", f.QPMvsWeek))
		} else {
			doc.WriteString(fmt.Sprintf(" 4:%.6f", f.QPMvsWeek))
		}
	} else {
		doc.WriteString(" 4:0")
	}

	if f.QPMvsMonth > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 5-QPMvsMonth:%.6f", f.QPMvsMonth))
		} else {
			doc.WriteString(fmt.Sprintf(" 5:%.6f", f.QPMvsMonth))
		}
	} else {
		doc.WriteString(" 5:0")
	}

	if f.QPMvsPrevMinute > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 6-QPMvsPrevMinute:%.6f", f.QPMvsPrevMinute))
		} else {
			doc.WriteString(fmt.Sprintf(" 6:%.6f", f.QPMvsPrevMinute))
		}
	} else {
		doc.WriteString(" 6:0")
	}

	if f.QPMvsLast20AvgFluctuation > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 7-QPMvsLast20AvgFluctuation:%.6f", f.QPMvsLast20AvgFluctuation))
		} else {
			doc.WriteString(fmt.Sprintf(" 7:%.6f", f.QPMvsLast20AvgFluctuation))
		}
	} else {
		doc.WriteString(" 7:0")
	}

	if debug {
		doc.WriteString(fmt.Sprintf(" 8-AVGIncrease:%d", f.AVGIncrease))
	} else {
		doc.WriteString(fmt.Sprintf(" 8:%d", f.AVGIncrease))
	}

	if f.AVGLast20Increase > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 9-AVGLast20Increase:%.6f", f.AVGLast20Increase))
		} else {
			doc.WriteString(fmt.Sprintf(" 9:%.6f", f.AVGLast20Increase))
		}
	} else {
		doc.WriteString(" 9:0")
	}

	if f.AVGvsPrevMinute > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 10-AVGvsPrevMinute:%.6f", f.AVGvsPrevMinute))
		} else {
			doc.WriteString(fmt.Sprintf(" 10:%.6f", f.AVGvsPrevMinute))
		}
	} else {
		doc.WriteString(" 10:0")
	}

	if f.AVGvsLast20AvgFluctuation > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 11-AVGvsLast20AvgFluctuation:%.6f", f.AVGvsLast20AvgFluctuation))
		} else {
			doc.WriteString(fmt.Sprintf(" 11:%.6f", f.AVGvsLast20AvgFluctuation))
		}
	} else {
		doc.WriteString(" 11:0")
	}

	if f.ThroughputvsPrevMinute > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 12-ThroughputvsPrevMinute:%.6f", f.ThroughputvsPrevMinute))
		} else {
			doc.WriteString(fmt.Sprintf(" 12:%.6f", f.ThroughputvsPrevMinute))
		}
	} else {
		doc.WriteString(" 12:0")
	}

	if f.ThroughputvsLast20AvgFluctuation > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 13-ThroughputvsLast20AvgFluctuation:%.6f", f.ThroughputvsLast20AvgFluctuation))
		} else {
			doc.WriteString(fmt.Sprintf(" 13:%.6f", f.ThroughputvsLast20AvgFluctuation))
		}
	} else {
		doc.WriteString(" 13:0")
	}

	if f.ErrorNum > 0 {
		if debug {
			doc.WriteString(fmt.Sprintf(" 14-ErrorNum:%d", f.ErrorNum))
		} else {
			doc.WriteString(fmt.Sprintf(" 14:%d", f.ErrorNum))
		}
	} else {
		doc.WriteString(" 14:0")
	}

	if f.ErrorvsPrevMinute > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 15-ErrorvsPrevMinute:%.6f", f.ErrorvsPrevMinute))
		} else {
			doc.WriteString(fmt.Sprintf(" 15:%.6f", f.ErrorvsPrevMinute))
		}
	} else {
		doc.WriteString(" 15:0")
	}

	if f.CPUBusyMax > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 16-CPUBusyMax:%.6f", f.CPUBusyMax))
		} else {
			doc.WriteString(fmt.Sprintf(" 16:%.6f", f.CPUBusyMax))
		}
	} else {
		doc.WriteString(" 16:0")
	}

	if debug {
		doc.WriteString(fmt.Sprintf(" 17-CPUBusyOverThreshold:%d", f.CPUBusyOverThreshold))
	} else {
		doc.WriteString(fmt.Sprintf(" 17:%d", f.CPUBusyOverThreshold))
	}

	if f.JVMFullgcCountMax > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 18-JVMFullgcCountMax:%.6f", f.JVMFullgcCountMax))
		} else {
			doc.WriteString(fmt.Sprintf(" 18:%.6f", f.JVMFullgcCountMax))
		}
	} else {
		doc.WriteString(" 18:0")
	}

	if debug {
		doc.WriteString(fmt.Sprintf(" 19-JVMFullgcCountOverThreshold:%d", f.JVMFullgcCountOverThreshold))
	} else {
		doc.WriteString(fmt.Sprintf(" 19:%d", f.JVMFullgcCountOverThreshold))
	}

	if f.JVMThreadBlockedCountMax > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 20-JVMThreadBlockedCountMax:%.6f", f.JVMThreadBlockedCountMax))
		} else {
			doc.WriteString(fmt.Sprintf(" 20:%.6f", f.JVMThreadBlockedCountMax))
		}
	} else {
		doc.WriteString(" 20:0")
	}

	if debug {
		doc.WriteString(fmt.Sprintf(" 21-JVMThreadBlockedCountOverThreshold:%d", f.JVMThreadBlockedCountOverThreshold))
	} else {
		doc.WriteString(fmt.Sprintf(" 21:%d", f.JVMThreadBlockedCountOverThreshold))
	}

	if f.MEMUsedPercentMax > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 22-MEMUsedPercentMax:%.6f", f.MEMUsedPercentMax))
		} else {
			doc.WriteString(fmt.Sprintf(" 22:%.6f", f.MEMUsedPercentMax))
		}
	} else {
		doc.WriteString(" 22:0")
	}

	if debug {
		doc.WriteString(fmt.Sprintf(" 23-MEMUsedPercentOverThreshold:%d", f.MEMUsedPercentOverThreshold))
	} else {
		doc.WriteString(fmt.Sprintf(" 23:%d", f.MEMUsedPercentOverThreshold))
	}

	if f.MEMSwapusedCountMax > float64(0.000001) {
		if debug {
			doc.WriteString(fmt.Sprintf(" 24-MEMSwapusedCountMax:%.6f", f.MEMSwapusedCountMax))
		} else {
			doc.WriteString(fmt.Sprintf(" 24:%.6f", f.MEMSwapusedCountMax))
		}
	} else {
		doc.WriteString(" 24:0")
	}

	if debug {
		doc.WriteString(fmt.Sprintf(" 25-MEMSwapusedCountOverThreshold:%d", f.MEMSwapusedCountOverThreshold))
	} else {
		doc.WriteString(fmt.Sprintf(" 25:%d", f.MEMSwapusedCountOverThreshold))
	}

	if debug {
		doc.WriteString(fmt.Sprintf(" 26-NumberOfMachines:%d", f.NumberOfMachines))
	} else {
		doc.WriteString(fmt.Sprintf(" 26:%d", f.NumberOfMachines))
	}
	doc.WriteString("\n")
	return doc.String()
}

// 通过机器学习预测判断
func (f *Features2) MLPredict() bool {
	fvals := make([]float64, 64)
	fvals[0] = float64(f.QPM)
	fvals[1] = f.QPMvsYesterday
	fvals[2] = f.QPMvsYesterdayMax
	fvals[3] = f.QPMvsWeek
	fvals[4] = f.QPMvsMonth
	fvals[5] = f.QPMvsPrevMinute
	fvals[6] = f.QPMvsLast20AvgFluctuation
	fvals[7] = float64(f.AVGIncrease)
	fvals[8] = f.AVGLast20Increase
	fvals[9] = f.AVGvsPrevMinute
	fvals[10] = f.AVGvsLast20AvgFluctuation
	fvals[11] = f.ThroughputvsPrevMinute
	fvals[12] = f.ThroughputvsLast20AvgFluctuation
	fvals[13] = float64(f.ErrorNum)
	fvals[14] = f.ErrorvsPrevMinute
	fvals[15] = f.CPUBusyMax
	fvals[16] = float64(f.CPUBusyOverThreshold)
	fvals[17] = f.JVMFullgcCountMax
	fvals[18] = float64(f.JVMFullgcCountOverThreshold)
	fvals[19] = f.JVMThreadBlockedCountMax
	fvals[20] = float64(f.JVMThreadBlockedCountOverThreshold)
	fvals[21] = f.MEMUsedPercentMax
	fvals[22] = float64(f.MEMUsedPercentOverThreshold)
	fvals[23] = f.MEMSwapusedCountMax
	fvals[24] = float64(f.MEMSwapusedCountOverThreshold)
	fvals[25] = float64(f.NumberOfMachines)
	return ml.Predict(fvals)
}

// 基于规则的风险判断
func (f *Features2) RiskByRule() (risk bool, ruleInfo string, targetArr []string) {

	if f == nil {
		return false, "", []string{}
	}

	// 超过10%的机器出现 cpu.busy
	if f.CPUBusyOverThreshold > 0 && float64(f.CPUBusyOverThreshold)/float64(f.NumberOfMachines) > 0.1 {
		return true, fmt.Sprintf("f.CPUBusyOverThreshold(%d) > 0 && float64(f.CPUBusyOverThreshold(%d))/float64(f.NumberOfMachines(%d)) > 0.1", f.CPUBusyOverThreshold, f.CPUBusyOverThreshold, f.NumberOfMachines), []string{"cpu.busy"}
	}

	// 高峰期GC才有问题
	if f.JVMFullgcCountOverThreshold > 1 && f.QPMvsYesterdayMax > 0.7 {
		return true, fmt.Sprintf("f.JVMFullgcCountOverThreshold(%d) > 1 && f.QPMvsYesterdayMax(%f) > 0.7", f.JVMFullgcCountOverThreshold, f.QPMvsYesterdayMax), []string{"jvm.fullgc.count", "qpm"}
	}

	// 日志记录会block线程, 只要不是高峰期的block就没有风险
	// 超过10%的机器有block线程问题,属于有风险
	if f.JVMThreadBlockedCountOverThreshold > int(float64(f.NumberOfMachines)*0.1) && f.QPMvsYesterdayMax > 0.6 {
		return true, fmt.Sprintf("f.JVMThreadBlockedCountOverThreshold(%d) >  int(float64(f.NumberOfMachines(%d))*0.1) && f.QPMvsYesterdayMax(%f) > 0.6  ", f.JVMThreadBlockedCountOverThreshold, f.NumberOfMachines, f.QPMvsYesterdayMax), []string{"jvm.thread.blocked.count"}
	}
	if f.MEMUsedPercentOverThreshold > 0 {
		return true, fmt.Sprintf("f.MEMUsedPercentOverThreshold(%d) > 0", f.MEMUsedPercentOverThreshold), []string{"mem.memused.percent"}
	}
	if f.MEMSwapusedCountOverThreshold > 0 {
		return true, fmt.Sprintf("f.MEMSwapusedCountOverThreshold(%d) > 0", f.MEMSwapusedCountOverThreshold), []string{"mem.swapused.percent"}
	}

	// 错误占比大
	if f.ErrorNum > 1 && f.ErrorvsPrevMinute > 2.0 && float64(f.ErrorNum)/float64(f.QPM) > 0.001 {
		return true, fmt.Sprintf("f.ErrorNum(%d) > 1 && f.ErrorvsPrevMinute(%f) > 2.0 &&  float64(f.ErrorNum(%d))/float64(f.QPM(%d))(%f)  > 0.001", f.ErrorNum, f.ErrorvsPrevMinute, f.ErrorNum, f.QPM, float64(f.ErrorNum)/float64(f.QPM)), []string{"error", "qpm"}
	}

	// 错误量级多
	if f.ErrorNum > 100 && f.ErrorvsPrevMinute > 2.0 {
		return true, fmt.Sprintf("f.ErrorNum(%d) > 100 && f.ErrorvsPrevMinute(%.3f) > 2.0", f.ErrorNum, f.ErrorvsPrevMinute), []string{"error"}
	}

	// 流量突增, 看耗时又没增加?
	// 20211108 上面的case, 有突增,但是耗时、服务指标各项正常, 不属于风险隐患
	if f.QPMvsPrevMinute > 1.4 && f.QPMvsYesterdayMax > 0.5 && f.AVGvsPrevMinute > 1.2 && (f.AVGvsLast20AvgFluctuation > 1.2 || f.CPUBusyOverThreshold > 1 || f.JVMFullgcCountOverThreshold > 1 || f.ErrorNum > 1) {
		return true, fmt.Sprintf("f.QPMvsPrevMinute(%f) > 1.4 && f.QPMvsYesterdayMax(%f) > 0.5  &&f.AVGvsPrevMinute(%f) > 1.2 && (f.AVGvsLast20AvgFluctuation(%f) > 1.2 || f.CPUBusyOverThreshold(%d) > 1 || f.JVMFullgcCountOverThreshold(%d) > 1 || f.ErrorNum(%d) > 1) ", f.QPMvsPrevMinute, f.QPMvsYesterdayMax, f.AVGvsPrevMinute, f.AVGvsLast20AvgFluctuation, f.CPUBusyOverThreshold, f.JVMFullgcCountOverThreshold, f.ErrorNum), []string{"qpm", "error", "avg", "cpu.busy", "jvm.fullgc.count"}
	}

	// 20211108 增加判断条件
	if f.QPMvsPrevMinute > 1.5 && f.QPMvsLast20AvgFluctuation > 1.3 && f.QPM > 1000 && (f.AVGvsLast20AvgFluctuation > 1.2 || f.CPUBusyOverThreshold > 1 || f.JVMFullgcCountOverThreshold > 1 || f.ErrorNum > 1) {
		return true, fmt.Sprintf("f.QPMvsPrevMinute(%f) > 1.5 && f.QPMvsLast20AvgFluctuation(%f) > 1.3 && f.QPM(%d) > 1000 && (f.AVGvsLast20AvgFluctuation(%f) > 1.2 || f.CPUBusyOverThreshold(%d) > 1 || f.JVMFullgcCountOverThreshold(%d) > 1 || f.ErrorNum(%d) > 1) ", f.QPMvsPrevMinute, f.QPMvsLast20AvgFluctuation, f.QPM, f.AVGvsLast20AvgFluctuation, f.CPUBusyOverThreshold, f.JVMFullgcCountOverThreshold, f.ErrorNum), []string{"qpm", "error", "avg", "cpu.busy", "jvm.fullgc.count"}
	}

	if f.AVGvsPrevMinute > 1.5 && f.AVGvsLast20AvgFluctuation > 1.3 && f.AVGIncrease > 50 {
		return true, fmt.Sprintf("f.AVGvsPrevMinute(%f) > 1.5 && f.AVGvsLast20AvgFluctuation(%f) > 1.3 && f.AVGIncrease(%d) > 50", f.AVGvsPrevMinute, f.AVGvsLast20AvgFluctuation, f.AVGIncrease), []string{"avg"}
	}

	if f.AVGvsPrevMinute > 1.5 && f.AVGvsLast20AvgFluctuation > 1.3 && f.AVGLast20Increase > 100 {
		return true, fmt.Sprintf("f.AVGvsPrevMinute(%f) > 1.5 && f.AVGvsLast20AvgFluctuation(%f) > 1.3 && f.AVGLast20Increase(%f) > 100", f.AVGvsPrevMinute, f.AVGvsLast20AvgFluctuation, f.AVGLast20Increase), []string{"avg"}
	}

	if f.ThroughputvsPrevMinute > 1.5 && f.ThroughputvsLast20AvgFluctuation > 1.3 && f.QPMvsYesterdayMax > 0.8 {
		return true, fmt.Sprintf("f.ThroughputvsPrevMinute(%f) > 1.5 && f.ThroughputvsLast20AvgFluctuation(%f) > 1.3 && f.QPMvsYesterdayMax(%f) > 0.8", f.ThroughputvsPrevMinute, f.ThroughputvsLast20AvgFluctuation, f.QPMvsYesterdayMax), []string{"avg", "qpm"}
	}

	// 解决这个问题 
	// 分钟级大范围上下波动,但是持续的都是这样的,属于无风险
	if f.QPMvsLast20AvgFluctuation < 1.3 {
		return false, fmt.Sprintf("f.QPMvsLast20AvgFluctuation(%f) < 1.3 ", f.QPMvsLast20AvgFluctuation), []string{}
	}

	// 20211106  流量比昨天和上周小的前提是没有错误,没有饱和度问题。
	// 所以这个放在最后
	if f.QPMvsYesterday < 1.1 || f.QPMvsWeek < 1.1 {
		return false, fmt.Sprintf("f.QPMvsYesterday(%f) < 1.1 || f.QPMvsWeek(%f) < 1.1", f.QPMvsYesterday, f.QPMvsWeek), []string{}
	}

	return false, "", []string{}
}

// 在已有数据基础上,伪造出一定有问题的负样本数据
// 这些问题不是边界问题,是必定有问题的。
func (f *Features2) Forge() {
	r := rand.New(rand.NewSource(time.Now().UnixNano())) // 随机数

	switch r.Intn(6) {
	case 0: // 超过10%的机器出现 cpu.busy
		f.CPUBusyMax = 80 + r.Float64()*20
		f.CPUBusyOverThreshold = int(float64(f.NumberOfMachines) * (0.3 + 0.7*r.Float64()))
	case 1: // 高峰期GC有问题
		f.QPMvsYesterdayMax = 80 + r.Float64()*20
		f.JVMFullgcCountMax = float64(f.NumberOfMachines) * (0.3 + 0.7*r.Float64())
		f.JVMFullgcCountOverThreshold = int(float64(f.NumberOfMachines) * (0.3 + 0.7*r.Float64()))
	case 2: // 超过10%的机器有block线程问题,属于有风险
		f.QPMvsYesterdayMax = 80 + r.Float64()*20
		f.JVMThreadBlockedCountMax = 20
		f.JVMThreadBlockedCountOverThreshold = int(float64(f.NumberOfMachines) * (0.3 + 0.7*r.Float64()))
	case 3: // 错误量级多
		f.ErrorNum = 100 + int(float64(f.QPM)*0.01*r.Float64())
		f.ErrorvsPrevMinute = float64(f.ErrorNum)
	case 4: // 流量突增, 耗时增加
		f.QPMvsPrevMinute = 2.0 + r.Float64()
		f.QPMvsYesterdayMax = 0.6 + 0.4*r.Float64()
		f.AVGvsLast20AvgFluctuation = 1.3 + r.Float64()
		f.AVGIncrease = int64(100 + r.Intn(1000))

	default: // 不伪造数据,直接返回
	}

}